Art Picross Game


Project Code

This code is written and rendered using Processing (v3), a quick and easy graphical sketchbook engine written in Java
class Cell {
	final static int BLANK = 0;
	final static int MARKED = 1;
	final static int CROSSED_OUT = 2;
	
	int col;
	int row;
	
	boolean filled = false;
	int state = Cell.BLANK;   // which state the cell is marked as
	
	Cell(int col_, int row_) {
		col = col_;
		row = row_;
		
		state = Cell.CROSSED_OUT;
	}
	
	void setFilled() {
		filled = true;
		state = Cell.MARKED;   // @TMP
	}
	
	void setFilled(boolean filled_) {
		filled = filled_;
	}
	
	boolean isFilled() {
		return filled;
	}
	
	void setState(int state_) {
		state = state_;
	}
	
	int getState() {
		return state;
	}
	
	boolean isMarked() {
		return (state == Cell.MARKED);
	}
	
	void nextState() {
		// set the state as the next state in the list
		if(state == Cell.BLANK) {
			state = Cell.MARKED;
		} else if(state == Cell.MARKED) {
			state = Cell.CROSSED_OUT;
		} else {
			// loop back
			state = Cell.BLANK;
		}
	}
	
	void render() {
		pushMatrix();
		pushStyle();
		
		translate(
			(col * cell_width),
			(row * cell_height)
		);
		
		stroke(0);
		strokeWeight(cell_border_size);
		fill(color_cell_bg);
		rect(0, 0, cell_width, cell_height);
		
		if(state == Cell.MARKED) {
			// marked
			fill(color_cell_fill);
			rect(cell_padding, cell_padding, (cell_width - (cell_padding * 2)), (cell_height - (cell_padding * 2)));
		} else if(state == Cell.CROSSED_OUT) {
			// crossed out
			pushStyle();
			
			stroke(color_cell_fill);
			strokeWeight(crossed_line_size);
			noFill();
			
			// offset padding for line
			float line_offset = (cell_padding + (crossed_line_size / 2));
			
			// top left to bottom right
			line(
				line_offset,
				line_offset,
				(cell_width - line_offset),
				(cell_height - line_offset)
			);
			
			// bottom left to top right
			line(
				line_offset,
				(cell_height - line_offset),
				(cell_width - line_offset),
				line_offset
			);
			
			popStyle();
		}
		
		popStyle();
		popMatrix();
	}
}
class Hint {
	final static int TOP = 0;
	final static int LEFT = 1;
	
	int type;
	int col;
	int row;
	String hint;
	
	Hint(int type_, int col_, int row_, String hint_) {
		type = type_;
		col = col_;
		row = row_;
		hint = hint_;
	}
	
	void render() {
		pushMatrix();
		pushStyle();
		
		translate(
			(col * cell_width),
			(row * cell_height)
		);
		
		stroke(0);
		strokeWeight(cell_border_size);
		fill(color_hint_bg);
		rect(0, 0, cell_width, cell_height);
		
		// display hint (if any)
		if(!hint.equals("")) {
			pushMatrix();
			pushStyle();
			
			translate(cell_border_size, cell_border_size);
			
			fill(color_hint_text);
			noStroke();
			textFont(hint_font);
			textSize(hint_font_size);
			textAlign(CENTER, BOTTOM);
			
			text(hint, (cell_width / 2), cell_height);
			
			popStyle();
			popMatrix();
		}
		
		popStyle();
		popMatrix();
	}
}
class Table {
	String name;
	int w;
	int h;
	
	Cell[][] cells;
	
	Hint[][] hints_top;
	Hint[][] hints_left;
	
	int max_hints_top = 0;   // max number of hint rows that show at the top
	int max_hints_left = 0;   // max number of hint columns that show on the left
	
	Table(String json_file) {
		loadJSON(json_file);
		buildStructure();
	}
	
	void loadJSON(String file) {
		JSONObject data = loadJSONObject(file);
		
		JSONObject table_settings = data.getJSONObject("table");
		name = table_settings.getString("name");
		w = table_settings.getInt("width");
		h = table_settings.getInt("height");
		
		// fill table with empty data
		cells = new Cell[h][w];
		
		for(int y = 0; y < h; y++) {
			for(int x = 0; x < w; x++) {
				cells[ y ][ x ] = new Cell(x, y);
			}
		}
		
		// load actual table data
		JSONArray table_rows = data.getJSONArray("data");
		
		for(int y = 0; y < table_rows.size(); y++) {
			if(y >= h) {
				break;
			}
			
			String row_data = table_rows.getString(y);
			
			for(int x = 0; x < row_data.length(); x++) {
				if(x >= w) {
					break;
				}
				
				if(row_data.substring(x, (x + 1)).equals("1")) {
					cells[ y ][ x ].setFilled();
				}
			}
		}
	}
	
	void buildStructure() {
		// get column counts
		ArrayList column_counts = new ArrayList();
		
		for(int x = 0; x < w; x++) {
			String count_data = "";
			int running_tally = 0;
			int num_hints = 0;
			
			for(int y = 0; y < h; y++) {
				Cell cell = cells[ y ][ x ];
				
				if(cell.isFilled()) {
					// increment running tally
					running_tally++;
					
					continue;
				}
				
				// not filled
				if(running_tally == 0) {
					// no running tally, just go on
					continue;
				}
				
				if(!count_data.equals("")) {
					count_data += ",";
				}
				
				count_data += running_tally;
				
				running_tally = 0;
				num_hints++;
			}
			
			// trailing tally
			if(running_tally > 0) {
				if(!count_data.equals("")) {
					count_data += ",";
				}
				
				count_data += running_tally;
				num_hints++;
			}
			
			column_counts.add(count_data);
			
			if(num_hints > max_hints_top) {
				max_hints_top = num_hints;
			}
		}
		
		// build hints_top
		hints_top = new Hint[ max_hints_top ][ w ];
		
		for(int x = 0; x < w; x++) {
			String raw_data = column_counts.get(x);
			
			String[] bits = raw_data.split(",");
			
			int offset = (max_hints_top - bits.length);
			
			for(int y = 0; y < max_hints_top; y++) {
				if(y < offset) {
					// empty hint
					hints_top[ y ][ x ] = new Hint(Hint.TOP, x, y, "");
					
					continue;
				}
				
				hints_top[ y ][ x ] = new Hint(Hint.TOP, x, y, bits[ (y - offset) ]);
			}
		}
		
		// get row counts
		ArrayList row_counts = new ArrayList();
		
		for(int y = 0; y < h; y++) {
			String count_data = "";
			int running_tally = 0;
			int num_hints = 0;
			
			for(int x = 0; x < w; x++) {
				Cell cell = cells[ y ][ x ];
				
				if(cell.isFilled()) {
					// increment running tally
					running_tally++;
					
					continue;
				}
				
				// not filled
				if(running_tally == 0) {
					// no running tally, just go on
					continue;
				}
				
				if(!count_data.equals("")) {
					count_data += ",";
				}
				
				count_data += running_tally;
				
				running_tally = 0;
				num_hints++;
			}
			
			// trailing tally
			if(running_tally > 0) {
				if(!count_data.equals("")) {
					count_data += ",";
				}
				
				count_data += running_tally;
				num_hints++;
			}
			
			row_counts.add(count_data);
			
			if(num_hints > max_hints_left) {
				max_hints_left = num_hints;
			}
		}
		
		// build hints_left
		hints_left = new Hint[ h ][ max_hints_left ];
		
		for(int y = 0; y < h; y++) {
			String raw_data = row_counts.get(y);
			
			String[] bits = raw_data.split(",");
			
			int offset = (max_hints_left - bits.length);
			
			for(int x = 0; x < max_hints_left; x++) {
				if(x < offset) {
					// empty hint
					hints_left[ y ][ x ] = new Hint(Hint.LEFT, x, y, "");
					
					continue;
				}
				
				hints_left[ y ][ x ] = new Hint(Hint.LEFT, x, y, bits[ (x - offset) ]);
			}
		}
	}
	
	int getWidth() {
		return ((max_hints_left * cell_width) + (w * cell_width));
	}
	
	int getHeight() {
		return ((max_hints_top * cell_height) + (h * cell_height));
	}
	
	void render() {
		render_preview();
		render_top_hints();
		render_left_hints();
		render_cells();
		render_dividing_lines();
	}
	
	void render_preview() {
		// preview in top left of existing filled in data
		
		float preview_width = ((cell_width * cells[0].length));
		float preview_height = ((cell_height * cells.length));
		
		PGraphics preview = createGraphics(int(preview_width), int(preview_height));
		
		preview.beginDraw();
		
		
		// bg
		preview.fill(color_cell_bg);
		preview.noStroke();
		
		preview.rect(0, 0, preview_width, preview_height);
		
		
		// cell previews
		preview.fill(color_cell_fill);
		preview.noStroke();
		
		for(int j = 0; j < cells.length; j++) {
			for(int k = 0; k < cells[ j ].length; k++) {
				if(cells[ j ][ k ].isMarked()) {
					preview.rect(
						(cell_width * k),
						(cell_height * j),
						cell_width,
						cell_height
					);
				}
			}
		}
		
		preview.endDraw();
		
		
		// render preview
		pushMatrix();
		pushStyle();
		
		float preview_container_width = ((cell_width * hints_left[0].length) - (divider_line_size / 2));
		float preview_container_height = ((cell_height * hints_top.length) - (divider_line_size / 2));
		
		float preview_border_size = 4.0;
		
		float max_sized_preview_width = preview_container_width;
		float max_sized_preview_height = preview_container_height;
		
		float sized_preview_width = 0;
		float sized_preview_height = 0;
		
		if(preview_height > preview_width) {
			// portrait, center x
			sized_preview_height = ((max_sized_preview_height * (preview_container_width / preview_container_height)) - (preview_border_size * 2));
			sized_preview_width = (preview_width * (sized_preview_height / preview_width));
		} else {
			// landscape or equal
			sized_preview_width = ((max_sized_preview_width * (preview_container_height / preview_container_width)) - (preview_border_size * 2));
			sized_preview_height = (preview_height * (sized_preview_width / preview_height));
		}
		
		translate(0, 0);
		
		// bg
		fill(color_cell_bg);
		noStroke();
		
		rect(0, 0, preview_container_width, preview_container_height);
		
		translate((preview_container_width / 2), (preview_container_height / 2));
		
		// preview
		image(
			preview,
			(-sized_preview_width / 2),
			(-sized_preview_height / 2),
			sized_preview_width,
			sized_preview_height
		);
		
		popStyle();
		popMatrix();
	}
	
	void render_top_hints() {
		int offset = (max_hints_left * cell_width);
		
		pushMatrix();
		
		translate(offset, 0);
		
		for(int j = 0; j < hints_top.length; j++) {
			for(int k = 0; k < hints_top[ j ].length; k++) {
				hints_top[ j ][ k ].render();
			}
		}
		
		popMatrix();
	}
	
	void render_left_hints() {
		int offset = (max_hints_top * cell_height);
		
		pushMatrix();
		
		translate(0, offset);
		
		for(int j = 0; j < hints_left.length; j++) {
			for(int k = 0; k < hints_left[ j ].length; k++) {
				hints_left[ j ][ k ].render();
			}
		}
		
		popMatrix();
	}
	
	void render_cells() {
		int offset_x = (max_hints_left * cell_width);
		int offset_y = (max_hints_top * cell_height);
		
		pushMatrix();
		
		translate(offset_x, offset_y);
		
		for(int j = 0; j < cells.length; j++) {
			for(int k = 0; k < cells[ j ].length; k++) {
				cells[ j ][ k ].render();
			}
		}
		
		popMatrix();
	}
	
	void render_dividing_lines() {
		int num_top_hints = hints_top.length;
		int num_left_hints = hints_left[0].length;
		int num_vertical_cells = cells.length;
		int num_horizontal_cells = cells[0].length;
		
		
		// horizontal lines
		int top_hint_offset = (cell_height * num_top_hints);
		int num_horizontal_lines = (1 + floor( (num_vertical_cells / 5) ));
		
		pushStyle();
		
		fill(color_divider_line);
		stroke(color_divider_line);
		strokeWeight(divider_line_size);
		
		for(int i = 0; i < num_horizontal_lines; i++) {
			int line_offset = (top_hint_offset + ((cell_height * 5) * i));
			
			line(0, line_offset, (cell_width * (num_left_hints + num_horizontal_cells)), line_offset);
		}
		
		popStyle();
		
		// vertical lines
		int left_hint_offset = (cell_width * num_left_hints);
		int num_vertical_lines = (1 + floor( (num_horizontal_cells / 5) ));
		
		pushStyle();
		
		fill(color_divider_line);
		stroke(color_divider_line);
		strokeWeight(divider_line_size);
		
		for(int i = 0; i < num_horizontal_lines; i++) {
			int line_offset = (left_hint_offset + ((cell_width * 5) * i));
			
			line(line_offset, 0, line_offset, (cell_height * (num_top_hints + num_vertical_cells)));
		}
		
		popStyle();
	}
}
Table table;

boolean scaling_enabled = false;
float current_scale = 1.0;
float scale_step = 0.9;

boolean dragging_enabled = false;
PVector drag_offset;

int cell_width = 32;
int cell_height = 32;
int cell_border_size = 1;
float cell_padding = 4.0;
float crossed_line_size = 3.0;

PFont hint_font;

int hint_font_size = 24;
int divider_line_size = 3;

color color_bg = color(30);
color color_border = color(0, 0, 0);
color color_hint_bg = color(233, 227, 178);
color color_hint_text = color(0, 0, 0);
color color_cell_bg = color(255, 255, 255);
color color_cell_fill = color(0, 0, 0);
color color_divider_line = color(0, 0, 0);

void setup() {
	//fullScreen(SPAN);
	//fullScreen();
	size(640, 640);
	surface.setResizable(true);
	
	hint_font = createFont("Roboto", hint_font_size);
	
	// load table
	//table = new Table("house.json");
	table = new Table("swan.json");
	//table = new Table("swan-reverse.json");
	
	// draw offsets
	drag_offset = new PVector();
	
	surface.setSize(table.getWidth(), table.getHeight());
}

void draw() {
	background(color_bg);
	
	if(scaling_enabled) {
		scale(current_scale);
	}
	
	if(scaling_enabled) {
		// not perfect
		translate(
			((width / 2) - (((table.getWidth() * current_scale) / 2) + drag_offset.x)),
			((height / 2) - (((table.getHeight() * current_scale) / 2) + drag_offset.y))
		);
	} else {
		translate(
			((width / 2) - (table.getWidth() / 2) + drag_offset.x),
			((height / 2) - (table.getHeight() / 2) + drag_offset.y)
		);
	}
	
	table.render();
	
	if(frameCount == 50) { save("preview.png"); }
}

void mouseWheel(MouseEvent event) {
	if(!scaling_enabled) {
		return;
	}
	
	float amt = event.getCount();
	
	if(amt < 0) {
		// scrolled up
		current_scale *= 1.1;
	} else {
		// scrolled down
		current_scale *= 0.9;
	}
}

void mouseDragged() {
	if(!dragging_enabled) {
		return;
	}
	
	int diff_x = (mouseX - pmouseX);
	int diff_y = (mouseY - pmouseY);
	
	drag_offset.add(diff_x, diff_y);
}
please visit our Contact Us page to get in touch
pyxol © 2021