godot-puzzle/state.gd
2025-04-29 18:14:44 -04:00

99 lines
2.3 KiB
GDScript

class_name State
extends Resource
@export var dims: Vector2i
@export var player_pos: Vector2i
@export var walls := BitMap.new()
@export var goals := BitMap.new()
@export var boxes := BitMap.new()
static func from_string(src: String) -> State:
var lines := src.lstrip("\n").rstrip("\n").split("\n")
var width := 0
for line in lines:
width = max(width, line.length())
var dims := Vector2i(width, lines.size())
var state := State.new()
state.dims = dims
state.walls.create(dims)
state.goals.create(dims)
state.boxes.create(dims)
for y in range(lines.size()):
var line := lines[y]
for x in range(line.length()):
var pos := Vector2i(x, y)
match line[x]:
"#": state.walls.set_bitv(pos, true)
".": state.goals.set_bitv(pos, true)
"@":
assert(not state.player_pos)
state.player_pos = pos
"+":
assert(not state.player_pos)
state.goals.set_bitv(pos, true)
state.player_pos = pos
"$": state.boxes.set_bitv(pos, true)
"*":
state.goals.set_bitv(pos, true)
state.boxes.set_bitv(pos, true)
assert(state.player_pos)
return state
func solid_at(pos: Vector2i) -> bool:
return wall_at(pos) or box_at(pos)
func wall_at(pos: Vector2i) -> bool:
return walls.get_bitv(pos)
func goal_at(pos: Vector2i) -> bool:
return goals.get_bitv(pos)
func box_at(pos: Vector2i) -> bool:
return boxes.get_bitv(pos)
func passable_at(pos: Vector2i) -> bool:
return not solid_at(pos)
# todo: keep shared structure (e.g. walls are always the same)
func step(move: Vector2i) -> State:
var new: State = duplicate(true)
var p0 := new.player_pos
var p1 := p0 + move
var p2 := p1 + move
if passable_at(p1):
new.player_pos = p1
elif box_at(p1) and passable_at(p2):
new.boxes.set_bitv(p1, false)
new.boxes.set_bitv(p2, true)
new.player_pos = p1
return new
func _to_string() -> String:
var out := "#<<state:{0}>, {1}@{2}\n".format([get_instance_id(), dims, player_pos])
for y in range(dims.y):
out += " "
for x in range(dims.x):
if player_pos == Vector2i(x, y):
out += "+" if goals.get_bit(x, y) else "@"
continue
if walls.get_bit(x, y):
out += "#"
continue
match [goals.get_bit(x, y), boxes.get_bit(x, y)]:
[true, true ]: out += "*"
[true, false]: out += "."
[false, true ]: out += "$"
[false, false]: out += " "
out += "\n"
out += ">"
return out