Compare commits
No commits in common. "498554be2946a21a8d2d27bd06200629d818f2c1" and "1d0a7f7befc835f9663ecf86a274039de50ff56a" have entirely different histories.
498554be29
...
1d0a7f7bef
9 changed files with 149 additions and 155 deletions
72
board.gd
72
board.gd
|
|
@ -1,72 +0,0 @@
|
||||||
class_name Board
|
|
||||||
extends Node3D
|
|
||||||
|
|
||||||
@export var dims: Vector2i
|
|
||||||
|
|
||||||
# idea: board has very little logic, delegates to something else
|
|
||||||
# l8r tho
|
|
||||||
|
|
||||||
# literally just linearly search to find pieces at a position
|
|
||||||
# children
|
|
||||||
|
|
||||||
# todo: Array[Piece]
|
|
||||||
func pieces() -> Array:
|
|
||||||
return get_children()
|
|
||||||
|
|
||||||
func pieces_at(pos: Vector2i) -> Array[Piece]:
|
|
||||||
var out: Array[Piece] = []
|
|
||||||
for piece in get_children():
|
|
||||||
if piece.lpos == pos:
|
|
||||||
out.push_back(piece)
|
|
||||||
return out
|
|
||||||
|
|
||||||
func find_piece_at(pos: Vector2i, filter: Callable) -> Piece:
|
|
||||||
var at := pieces_at(pos)
|
|
||||||
var idx := at.find_custom(filter)
|
|
||||||
if idx < 0: return
|
|
||||||
return at[idx]
|
|
||||||
|
|
||||||
func any_at(pos: Vector2i, filter: Callable) -> bool:
|
|
||||||
return pieces_at(pos).any(filter)
|
|
||||||
|
|
||||||
func solid_at(pos: Vector2i) -> bool:
|
|
||||||
return any_at(pos, func(p): return p.type == Piece.Type.Wall or p.type == Piece.Type.Box)
|
|
||||||
|
|
||||||
func passable_at(pos: Vector2i) -> bool:
|
|
||||||
return not solid_at(pos)
|
|
||||||
|
|
||||||
func remove_piece(piece: Piece):
|
|
||||||
remove_child(piece)
|
|
||||||
|
|
||||||
func add_piece(piece: Piece):
|
|
||||||
add_child(piece)
|
|
||||||
|
|
||||||
func add_pieces(piece: Array[Piece]):
|
|
||||||
for p in piece:
|
|
||||||
add_piece(p)
|
|
||||||
|
|
||||||
static func from_string(src: String) -> Board:
|
|
||||||
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 board := Board.new()
|
|
||||||
board.dims = dims
|
|
||||||
|
|
||||||
for y in range(lines.size()):
|
|
||||||
var line := lines[y]
|
|
||||||
for x in range(line.length()):
|
|
||||||
var c := line[x]
|
|
||||||
var pos := Vector2i(x, y)
|
|
||||||
match c:
|
|
||||||
".": board.add_piece(Piece.goal(pos))
|
|
||||||
"*": board.add_pieces([Piece.goal(pos), Piece.box(pos)])
|
|
||||||
"+": board.add_pieces([Piece.goal(pos), Piece.player(pos)])
|
|
||||||
"$": board.add_piece(Piece.box(pos))
|
|
||||||
"@": board.add_piece(Piece.player(pos))
|
|
||||||
"#": board.add_piece(Piece.wall(pos))
|
|
||||||
return board
|
|
||||||
|
|
@ -1 +0,0 @@
|
||||||
uid://c8ywa33v3jq7t
|
|
||||||
74
main.gd
74
main.gd
|
|
@ -10,17 +10,37 @@ var microban_1 := "
|
||||||
####
|
####
|
||||||
"
|
"
|
||||||
|
|
||||||
var hist := UndoRedo.new()
|
var states: Array[State] = [State.from_string(microban_1)]
|
||||||
|
|
||||||
@onready var board := Board.from_string(microban_1)
|
const WALL = preload("res://wall.tres")
|
||||||
var player: Piece
|
const GOAL = preload("res://goal.tres")
|
||||||
|
const BOX = preload("res://box.tres")
|
||||||
|
const PLAYER = preload("res://player.tres")
|
||||||
|
|
||||||
|
@onready var board: Node3D = $Board
|
||||||
|
|
||||||
func _ready() -> void:
|
func _ready() -> void:
|
||||||
add_child(board)
|
render()
|
||||||
for piece in board.pieces():
|
|
||||||
if piece.type == Piece.Type.Player:
|
func add_mesh(pos: Vector2i, mesh: Mesh):
|
||||||
player = piece
|
var offset := -current_state().dims/2.0
|
||||||
break
|
var meshinst := MeshInstance3D.new()
|
||||||
|
meshinst.position = Vector3(pos.x + offset.x, 0, pos.y + offset.y)
|
||||||
|
meshinst.mesh = mesh
|
||||||
|
board.add_child(meshinst)
|
||||||
|
|
||||||
|
func render() -> void:
|
||||||
|
var state := current_state()
|
||||||
|
for child in board.get_children():
|
||||||
|
child.free()
|
||||||
|
|
||||||
|
for y in state.dims.y:
|
||||||
|
for x in state.dims.x:
|
||||||
|
var pos := Vector2i(x, y)
|
||||||
|
if state.goal_at(pos): add_mesh(pos, GOAL)
|
||||||
|
if state.box_at(pos): add_mesh(pos, BOX)
|
||||||
|
if state.wall_at(pos): add_mesh(pos, WALL)
|
||||||
|
if pos == state.player_pos: add_mesh(pos, PLAYER)
|
||||||
|
|
||||||
func _input(event: InputEvent) -> void:
|
func _input(event: InputEvent) -> void:
|
||||||
if event.is_action_pressed("u"):
|
if event.is_action_pressed("u"):
|
||||||
|
|
@ -32,29 +52,29 @@ func _input(event: InputEvent) -> void:
|
||||||
elif event.is_action_pressed("r"):
|
elif event.is_action_pressed("r"):
|
||||||
step(Vector2i.RIGHT)
|
step(Vector2i.RIGHT)
|
||||||
elif event.is_action_pressed("undo"):
|
elif event.is_action_pressed("undo"):
|
||||||
hist.undo()
|
undo()
|
||||||
elif event.is_action_pressed("redo"):
|
elif event.is_action_pressed("redo"):
|
||||||
hist.redo()
|
pass
|
||||||
elif event.is_action_pressed("restart"):
|
elif event.is_action_pressed("restart"):
|
||||||
restart()
|
restart()
|
||||||
|
|
||||||
|
|
||||||
# TODO:
|
func current_state() -> State:
|
||||||
|
return states[-1]
|
||||||
|
|
||||||
|
func push_state(state: State):
|
||||||
|
states.push_back(state)
|
||||||
|
render()
|
||||||
|
|
||||||
|
func undo():
|
||||||
|
if states.size() == 1:
|
||||||
|
return
|
||||||
|
states.pop_back()
|
||||||
|
render()
|
||||||
|
|
||||||
|
# todo: don't restart multiple times
|
||||||
func restart():
|
func restart():
|
||||||
pass
|
push_state(states[0])
|
||||||
|
|
||||||
func step(move: Vector2i):
|
func step(move: Vector2i):
|
||||||
var pos := player.lpos
|
push_state(current_state().step(move))
|
||||||
var box := board.find_piece_at(pos + move, func(p): return p.type == Piece.Type.Box)
|
|
||||||
if board.passable_at(pos + move):
|
|
||||||
hist.create_action("move")
|
|
||||||
hist.add_do_method(player.do_move(move))
|
|
||||||
hist.add_undo_method(player.undo_move())
|
|
||||||
hist.commit_action()
|
|
||||||
elif box != null and board.passable_at(pos + move * 2):
|
|
||||||
hist.create_action("push")
|
|
||||||
hist.add_do_method(player.do_move(move))
|
|
||||||
hist.add_do_method(box.do_move(move))
|
|
||||||
hist.add_undo_method(player.undo_move())
|
|
||||||
hist.add_undo_method(box.undo_move())
|
|
||||||
hist.commit_action()
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -16,8 +16,10 @@ sky = SubResource("Sky_0xm2m")
|
||||||
script = ExtResource("1_ig7tw")
|
script = ExtResource("1_ig7tw")
|
||||||
|
|
||||||
[node name="Camera3D" type="Camera3D" parent="."]
|
[node name="Camera3D" type="Camera3D" parent="."]
|
||||||
transform = Transform3D(1, 0, 0, 0, 0.258819, 0.965926, 0, -0.965926, 0.258819, 0.035, 3.615, 3)
|
transform = Transform3D(1, 0, 0, 0, 0.258819, 0.965926, 0, -0.965926, 0.258819, 0, 3.48438, 1.16423)
|
||||||
fov = 90.0
|
fov = 90.0
|
||||||
|
|
||||||
[node name="WorldEnvironment" type="WorldEnvironment" parent="."]
|
[node name="WorldEnvironment" type="WorldEnvironment" parent="."]
|
||||||
environment = SubResource("Environment_ig7tw")
|
environment = SubResource("Environment_ig7tw")
|
||||||
|
|
||||||
|
[node name="Board" type="Node3D" parent="."]
|
||||||
|
|
|
||||||
52
piece.gd
52
piece.gd
|
|
@ -1,52 +0,0 @@
|
||||||
class_name Piece
|
|
||||||
extends MeshInstance3D
|
|
||||||
|
|
||||||
const WALL = preload("res://wall.tres")
|
|
||||||
const GOAL = preload("res://goal.tres")
|
|
||||||
const BOX = preload("res://box.tres")
|
|
||||||
const PLAYER = preload("res://player.tres")
|
|
||||||
|
|
||||||
enum Type {
|
|
||||||
Wall,
|
|
||||||
Goal,
|
|
||||||
Box,
|
|
||||||
Player,
|
|
||||||
}
|
|
||||||
|
|
||||||
# logical position
|
|
||||||
@export var lpos: Vector2i:
|
|
||||||
get:
|
|
||||||
return lpos
|
|
||||||
set(val):
|
|
||||||
lpos = val
|
|
||||||
position = Vector3(val.x, 0, val.y)
|
|
||||||
|
|
||||||
@export var type: Piece.Type
|
|
||||||
|
|
||||||
func do_move(move: Vector2i) -> Callable:
|
|
||||||
return func():
|
|
||||||
lpos += move
|
|
||||||
|
|
||||||
func undo_move() -> Callable:
|
|
||||||
var old_pos := lpos
|
|
||||||
return func():
|
|
||||||
lpos = old_pos
|
|
||||||
|
|
||||||
static func make(lpos: Vector2i, type: Piece.Type, mesh: Mesh) -> Piece:
|
|
||||||
var piece := Piece.new()
|
|
||||||
piece.lpos = lpos
|
|
||||||
piece.mesh = mesh
|
|
||||||
piece.type = type
|
|
||||||
return piece
|
|
||||||
|
|
||||||
static func wall(lpos: Vector2i) -> Piece:
|
|
||||||
return make(lpos, Piece.Type.Wall, WALL)
|
|
||||||
|
|
||||||
static func goal(lpos: Vector2i) -> Piece:
|
|
||||||
return make(lpos, Piece.Type.Goal, GOAL)
|
|
||||||
|
|
||||||
static func box(lpos: Vector2i) -> Piece:
|
|
||||||
return make(lpos, Piece.Type.Box, BOX)
|
|
||||||
|
|
||||||
static func player(lpos: Vector2i) -> Piece:
|
|
||||||
return make(lpos, Piece.Type.Player, PLAYER)
|
|
||||||
|
|
@ -1 +0,0 @@
|
||||||
uid://bq3a5hhccxndn
|
|
||||||
|
|
@ -74,7 +74,6 @@ redo={
|
||||||
, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":true,"pressed":false,"keycode":0,"physical_keycode":89,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null)
|
, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":true,"pressed":false,"keycode":0,"physical_keycode":89,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null)
|
||||||
, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":true,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":85,"key_label":0,"unicode":85,"location":0,"echo":false,"script":null)
|
, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":true,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":85,"key_label":0,"unicode":85,"location":0,"echo":false,"script":null)
|
||||||
, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":true,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":90,"key_label":0,"unicode":90,"location":0,"echo":false,"script":null)
|
, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":true,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":90,"key_label":0,"unicode":90,"location":0,"echo":false,"script":null)
|
||||||
, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":80,"key_label":0,"unicode":112,"location":0,"echo":false,"script":null)
|
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
98
state.gd
Normal file
98
state.gd
Normal file
|
|
@ -0,0 +1,98 @@
|
||||||
|
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
|
||||||
1
state.gd.uid
Normal file
1
state.gd.uid
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
uid://bav3tjhu6anx
|
||||||
Loading…
Reference in a new issue