extends ColorRect export var rows: int = 44 export var cols: int = 63 export var h_margin: int = 2 export var v_margin: int = 2 export var grid_color: Color = Color.black var READY := false const ByteArray2D = Common.ByteArray2D const IntArray2D = Common.IntArray2D onready var cell_numbers: ByteArray2D const NUMBER_NONE := 255 onready var cell_colors: ByteArray2D onready var corner_marks: IntArray2D enum CornerMark { L_RIGHT=1, L_DOWN=2, X_RIGHT=4, X_DOWN=8, DARC_1=16, DARC_2=32, DARC_3=64, DARC_4=128, SARC_1=256, SARC_2=512, SARC_3=1024, SARC_4=2048 } func _ready(): # load_puzzle('res://slither_blank.txt') load_puzzle('res://slither-202005192059.txt') func draw_string_centered(font, position, string, color := Color.white): draw_string(font, Vector2(position.x - font.get_string_size(string).x/2.0, position.y + font.get_ascent()), string, color) const font := preload('res://dynamicfont.tres') const COLORS = [null, Color.lightgreen, Color.lightyellow, Color.lightblue, Color.purple] const N_COLORS = 5 var grid_0 := Vector2(0.0, 0.0) var grid_space := Vector2(1.0, 1.0) var h0 := 0.0 var v0 := 0.0 var h_space := 1.0 var v_space := 1.0 func update_grid_spacing() -> void: h_space = rect_size.x / (h_margin * 2 + cols) v_space = rect_size.y / (v_margin * 2 + rows) if h_space > v_space: h_space = v_space else: v_space = h_space h0 = h_space * h_margin v0 = v_space * v_margin grid_0 = Vector2(h0, v0) grid_space = Vector2(h_space, v_space) func grid_corner(row, col) -> Vector2: return (Vector2(col, row) * grid_space) + grid_0 func to_grid_space(position: Vector2) -> Vector2: return (position - grid_0)/grid_space func _draw() -> void: if not READY: return update_grid_spacing() font.set_size(int(ceil(v_space-5))) # Colors for row in rows: for col in cols: var color = COLORS[cell_colors.get_cell(row, col)] if color != null: draw_rect(Rect2(grid_corner(row, col), Vector2(h_space, v_space)), color, true) # Lines for row in rows: for col in cols: if corner_marks.get_flag(row, col, CornerMark.L_RIGHT): draw_line(grid_corner(row, col), grid_corner(row, col+1), Color.blue) elif corner_marks.get_flag(row, col, CornerMark.X_RIGHT): draw_line(grid_corner(row, col+0.4), grid_corner(row, col+0.6), Color.red) else: draw_line(grid_corner(row, col), grid_corner(row, col+1), Color.white) if corner_marks.get_flag(row, col, CornerMark.L_DOWN): draw_line(grid_corner(row, col), grid_corner(row+1, col), Color.blue) elif corner_marks.get_flag(row, col, CornerMark.X_DOWN): draw_line(grid_corner(row+0.4, col), grid_corner(row+0.6, col), Color.red) else: draw_line(grid_corner(row, col), grid_corner(row+1, col), Color.white) for col in cols: if corner_marks.get_flag(rows, col, CornerMark.L_RIGHT): draw_line(grid_corner(rows, col), grid_corner(rows, col+1), Color.blue) elif corner_marks.get_flag(rows, col, CornerMark.X_RIGHT): draw_line(grid_corner(rows, col+0.4), grid_corner(rows, col+0.6), Color.red) else: draw_line(grid_corner(rows, col), grid_corner(rows, col+1), Color.white) for row in rows: if corner_marks.get_flag(row, cols, CornerMark.L_DOWN): draw_line(grid_corner(row, cols), grid_corner(row+1, cols), Color.blue) elif corner_marks.get_flag(row, cols, CornerMark.X_DOWN): draw_line(grid_corner(row+0.4, cols), grid_corner(row+0.6, cols), Color.red) else: draw_line(grid_corner(row, cols), grid_corner(row+1, cols), Color.white) # Arc pencil marks # Numbers for row in rows: for col in cols: var num = cell_numbers.get_cell(row, col) if num < 4: draw_string_centered(font, grid_corner(row, col+0.5), str(num), Color.black) # COORDS for row in rows: draw_string_centered(font, Vector2(h0/2, v0+row*v_space), '%02d'%row, Color.black) for col in cols: draw_string_centered(font, Vector2(h0+(col+0.5)*h_space, v0/4), num2alpha(col), Color.black) func num2alpha(num: int, uppercase:=false): var c = ord('A' if uppercase else 'a') if num >= 26: return char(c+(num/26)-1) + char(c+(num%26)) else: return char(c+num) func _process(delta: float) -> void: update() const num_dict := {'.': NUMBER_NONE, '0': 0, '1': 1, '2': 2, '3': 3} const colornum_dict := {'0': 0, '1': 1, '2': 2} func load_puzzle(filename: String): var file := File.new() var error = file.open(filename, File.READ) if file.get_line() != 'pzprv3.1': return if file.get_line() != 'slither': return rows = int(file.get_line()) cols = int(file.get_line()) cell_numbers = ByteArray2D.new(rows, cols) cell_colors = ByteArray2D.new(rows, cols) corner_marks = IntArray2D.new(rows+1, cols+1, 0) var row = 0 var stage = 0 var until = [rows, rows, rows, rows+1, 0] while not file.eof_reached(): if row >= until[stage]: stage += 1 row = 0 match stage: 0: # Puzzle definition numbers var line = file.get_csv_line(' ') var col = 0 for numstr in line: if col < cols: cell_numbers.set_cell(row, col, num_dict.get(numstr, NUMBER_NONE)) col += 1 1: # cell colors var line = file.get_csv_line(' ') var col = 0 for numstr in line: if col < cols: cell_colors.set_cell(row, col, colornum_dict.get(numstr, 0)) col += 1 2: # vert lines var line = file.get_csv_line(' ') var col = 0 for numstr in line: if col < cols+1: match int(numstr): 1: corner_marks.set_flag(row, col, CornerMark.L_DOWN) -1: corner_marks.set_flag(row, col, CornerMark.X_DOWN) col += 1 3: # horiz lines var line = file.get_csv_line(' ') var col = 0 for numstr in line: if col < cols+1: match int(numstr): 1: corner_marks.set_flag(row, col, CornerMark.L_RIGHT) -1: corner_marks.set_flag(row, col, CornerMark.X_RIGHT) col += 1 4: break row += 1 file.close() READY = true enum DragAction {TEST_LINE, DRAW_LINE, REMOVE_LINE, DRAW_X, REMOVE_X, DRAW_COLOR} var drag_action = 0 var drag_color := 0 func _input(event: InputEvent) -> void: if event is InputEventMouse: var gridpos = to_grid_space(event.position) var dx = fmod(gridpos.x, 1.0) var dy = fmod(gridpos.y, 1.0) var adx = 0.5 - abs(dx-0.5) var ady = 0.5 - abs(dy-0.5) var threshold = 0.2 var sel_col var sel_row if event is InputEventMouseButton and event.pressed: if adx < threshold: if ady < threshold: drag_action = DragAction.TEST_LINE else: sel_row = int(gridpos.y) sel_col = int(round(gridpos.x)) if corner_marks.get_flag(sel_row, sel_col, CornerMark.X_DOWN): drag_action = DragAction.REMOVE_X corner_marks.clear_flag(sel_row, sel_col, CornerMark.X_DOWN) else: drag_action = DragAction.DRAW_X corner_marks.set_flag(sel_row, sel_col, CornerMark.X_DOWN) else: if ady < threshold: sel_row = int(round(gridpos.y)) sel_col = int(gridpos.x) if corner_marks.get_flag(sel_row, sel_col, CornerMark.X_RIGHT): drag_action = DragAction.REMOVE_X corner_marks.clear_flag(sel_row, sel_col, CornerMark.X_RIGHT) else: drag_action = DragAction.DRAW_X corner_marks.set_flag(sel_row, sel_col, CornerMark.X_RIGHT) else: sel_row = int(gridpos.y) sel_col = int(gridpos.x) drag_action = DragAction.DRAW_COLOR drag_color = posmod(cell_colors.get_cell(sel_row, sel_col) + (1 if event.button_index==BUTTON_LEFT else -1), N_COLORS) cell_colors.set_cell(sel_row, sel_col, drag_color) elif event is InputEventMouseMotion and event.button_mask: if drag_action == DragAction.TEST_LINE: if adx < threshold and ady > threshold: drag_action = DragAction.REMOVE_LINE if corner_marks.get_flag(int(gridpos.y), int(round(gridpos.x)), CornerMark.L_DOWN) else DragAction.DRAW_LINE elif adx > threshold and ady < threshold: drag_action = DragAction.REMOVE_LINE if corner_marks.get_flag(int(round(gridpos.y)), int(gridpos.x), CornerMark.L_RIGHT) else DragAction.DRAW_LINE match drag_action: DragAction.DRAW_LINE: if adx < threshold and ady > threshold: set_line_down(int(gridpos.y), int(round(gridpos.x))) elif adx > threshold and ady < threshold: set_line_right(int(round(gridpos.y)), int(gridpos.x)) DragAction.REMOVE_LINE: if adx < threshold and ady > threshold: clear_line_down(int(gridpos.y), int(round(gridpos.x))) elif adx > threshold and ady < threshold: clear_line_right(int(round(gridpos.y)), int(gridpos.x)) DragAction.DRAW_X: if adx < threshold and ady > threshold: set_x_down(int(gridpos.y), int(round(gridpos.x))) elif adx > threshold and ady < threshold: set_x_right(int(round(gridpos.y)), int(gridpos.x)) DragAction.REMOVE_X: if adx < threshold and ady > threshold: clear_x_down(int(gridpos.y), int(round(gridpos.x))) elif adx > threshold and ady < threshold: clear_x_right(int(round(gridpos.y)), int(gridpos.x)) DragAction.DRAW_COLOR: sel_row = int(gridpos.y) sel_col = int(gridpos.x) cell_colors.set_cell(sel_row, sel_col, drag_color) var undo_stack = [] func set_line_right(row, col): if !corner_marks.get_flag(row, col, CornerMark.L_RIGHT) and !corner_marks.get_flag(row, col, CornerMark.X_RIGHT): corner_marks.set_flag(row, col, CornerMark.L_RIGHT) # TODO: Add undo stack entry func set_line_down(row, col): if !corner_marks.get_flag(row, col, CornerMark.L_DOWN) and !corner_marks.get_flag(row, col, CornerMark.X_DOWN): corner_marks.set_flag(row, col, CornerMark.L_DOWN) func clear_line_right(row, col): if corner_marks.get_flag(row, col, CornerMark.L_RIGHT): corner_marks.clear_flag(row, col, CornerMark.L_RIGHT) func clear_line_down(row, col): if corner_marks.get_flag(row, col, CornerMark.L_DOWN): corner_marks.clear_flag(row, col, CornerMark.L_DOWN) func set_x_right(row, col): if !corner_marks.get_flag(row, col, CornerMark.L_RIGHT) and !corner_marks.get_flag(row, col, CornerMark.X_RIGHT): corner_marks.set_flag(row, col, CornerMark.X_RIGHT) func set_x_down(row, col): if !corner_marks.get_flag(row, col, CornerMark.L_DOWN) and !corner_marks.get_flag(row, col, CornerMark.X_DOWN): corner_marks.set_flag(row, col, CornerMark.X_DOWN) func clear_x_right(row, col): if corner_marks.get_flag(row, col, CornerMark.X_RIGHT): corner_marks.clear_flag(row, col, CornerMark.X_RIGHT) func clear_x_down(row, col): if corner_marks.get_flag(row, col, CornerMark.X_DOWN): corner_marks.clear_flag(row, col, CornerMark.X_DOWN)