1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
|
#lang rhombus/static
export:
Chunk
Entity
World
Tile
enum Direction:
north
north_east
east
south_east
south
south_west
west
north_west
enum TileType:
empty
wall_north
fun flip_block(block :: maybe(Direction)) :: maybe(Direction):
match block
| #'north: #'south
| #'north_east: #'south_west
| #'east: #'west
| #'south_east: #'north_west
| #'south: #'north
| #'south_west: #'north_east
| #'west: #'east
| #'north_west: #'south_east
| ~else: #false
class Tile(type :: TileType):
method get_block() :: maybe(Direction):
match type
| #'empty: #false
| #'wall_north: #'north
class Chunk(width :: Int,
height :: Int,
offset_x :: Int,
offset_y :: Int,
tiles :: Array.now_of(Tile),
neighbours :: MutableMap.now_of(Direction, Chunk)):
constructor(width :: Int, height :: Int, offset_x :: Int, offset_y :: Int):
super(width, height, offset_x, offset_y, Array.make(width * height, Tile(#'empty)), MutableMap())
method add_neighbour(chunk :: Chunk, direction :: Direction):
neighbours[direction] := chunk
method get_tile(x :: Int, y :: Int) :: Tile:
tiles[y * width + x]
method set_tile(x :: Int, y :: Int, tile :: Tile):
tiles[y * width + x] := tile
class Entity(id:: Int, mutable x :: Int, mutable y :: Int, mutable current_chunk :: Chunk):
method
| move(x :: Int, y :: Int): move(x, y, 1)
| move(x :: Int, y :: Int, max_dist :: Int) :: Boolean:
let mutable target_chunk :: Chunk = current_chunk
let mutable target_tile = Tile(#'empty)
let validate_dist = fun (entity :: Entity) :: Boolean:
let diff_x = math.abs(entity.x - x)
let diff_y = math.abs(entity.y - y)
// Diagonal case is moving in the same direction (same x & y) and that amount
// is less than or equal to max distance
let diagonal = diff_x == diff_y && diff_x <= max_dist
let x_axis = diff_x <= max_dist && diff_y == 0
let y_axis = diff_y <= max_dist && diff_x == 0
let no_movement = diff_x == 0 && diff_y == 0
!no_movement && (diagonal || x_axis || y_axis)
let validate_chunk = fun (entity :: Entity) :: Boolean:
let current_chunk = entity.current_chunk
let local_x = x - current_chunk.offset_x * current_chunk.width
let local_y = y - current_chunk.offset_y * current_chunk.height
let less_x = local_x < 0
let less_y = local_y < 0
let more_x = local_x >= current_chunk.width
let more_y = local_y >= current_chunk.height
let inside_x = !(less_x || more_x)
let inside_y = !(less_y || more_y)
let next_chunk :: maybe(Chunk) = cond
| inside_x && less_y: current_chunk.neighbours.get(#'north, #false)
| more_x && less_y: current_chunk.neighbours.get(#'north_east, #false)
| more_x && inside_y: current_chunk.neighbours.get(#'east, #false)
| more_x && more_y: current_chunk.neighbours.get(#'south_east, #false)
| inside_x && more_y: current_chunk.neighbours.get(#'south, #false)
| less_x && more_y: current_chunk.neighbours.get(#'south_west, #false)
| less_x && inside_y: current_chunk.neighbours.get(#'west, #false)
| less_x && less_y: current_chunk.neighbours.get(#'north_west, #false)
| ~else: current_chunk
cond
| !next_chunk: #false
| ~else:
target_chunk := next_chunk!!
let local_x = x - current_chunk.offset_x * current_chunk.width
let local_y = y - current_chunk.offset_y * current_chunk.height
target_tile := entity.current_chunk.get_tile(local_x, local_y)
#true
let validate_move = fun (entity :: Entity) :: Boolean:
let current_tile = entity.current_chunk.get_tile(entity.x, entity.y)
let target_tile = target_chunk.get_tile(x, y)
let diff_x = x - this.x
let diff_y = y - this.y
let direction = cond
| diff_x == 0 && diff_y < 0: #'north
| diff_x > 0 && diff_y < 0: #'north_east
| diff_x > 0 && diff_y == 0: #'east
| diff_x > 0 && diff_y > 0: #'south_east
| diff_x == 0 && diff_y > 0: #'south
| diff_x < 0 && diff_y > 0: #'south_west
| diff_x < 0 && diff_y == 0: #'west
| diff_x < 0 && diff_y < 0: #'north_west
let current_block = current_tile.get_block()
let target_block = flip_block(target_tile.get_block())
!(direction == current_block || direction == target_block)
cond
| validate_dist(this) && validate_chunk(this) && validate_move(this):
this.current_chunk := target_chunk
this.x := x
this.y := y
#true
| ~else:
#false
class World(chunks :: MutableList.now_of(Chunk),
entities :: MutableList.now_of(Entity)):
constructor():
super(MutableList(), MutableList())
|