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
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
| package streams
import common._
/**
* This trait represents the layout and building blocks of the game
*/
trait GameDef {
/**
* The case class `Pos` encodes positions in the terrain.
*
* IMPORTANT NOTE
* - The `row` coordinate denotes the position on the vertical axis
* - The `col` coordinate is used for the horizontal axis
* - The coordinates increase when moving down and right
*
* Illustration:
*
* 0 1 2 3 <- col axis
* 0 o o o o
* 1 o o o o
* 2 o # o o # is at position Pos(2, 1)
* 3 o o o o
*
* ^
* |
*
* row axis
*/
case class Pos(row: Int, col: Int) {
/** The position obtained by changing the `row` coordinate by `d` */
def deltaRow(d: Int): Pos = copy(row = row + d)
/** The position obtained by changing the `col` coordinate by `d` */
def deltaCol(d: Int): Pos = copy(col = col + d)
}
/**
* The position where the block is located initially.
*
* This value is left abstract, it will be defined in concrete
* instances of the game.
*/
val startPos: Pos
/**
* The target position where the block has to go.
* This value is left abstract.
*/
val goal: Pos
/**
* The terrain is represented as a function from positions to
* booleans. The function returns `true` for every position that
* is inside the terrain.
*
* As explained in the documentation of class `Pos`, the `row` axis
* is the vertical one and increases from top to bottom.
*/
type Terrain = Pos => Boolean
/**
* The terrain of this game. This value is left abstract.
*/
val terrain: Terrain
/**
* In Bloxorz, we can move left, right, Up or down.
* These moves are encoded as case objects.
*/
sealed abstract class Move
case object Left extends Move
case object Right extends Move
case object Up extends Move
case object Down extends Move
/**
* This function returns the block at the start position of
* the game.
*/
def startBlock: Block = Block(startPos, startPos)
/**
* A block is represented by the position of the two cubes that
* it consists of. We make sure that `b1` is lexicographically
* smaller than `b2`.
*/
case class Block(b1: Pos, b2: Pos) {
// checks the requirement mentioned above
require(b1.row <= b2.row && b1.col <= b2.col, "Invalid block position: b1=" + b1 + ", b2=" + b2)
/**
* Returns a block where the `row` coordinates of `b1` and `b2` are
* changed by `d1` and `d2`, respectively.
*/
def deltaRow(d1: Int, d2: Int): Block = Block(b1.deltaRow(d1), b2.deltaRow(d2))
/**
* Returns a block where the `col` coordinates of `b1` and `b2` are
* changed by `d1` and `d2`, respectively.
*/
def deltaCol(d1: Int, d2: Int): Block = Block(b1.deltaCol(d1), b2.deltaCol(d2))
/** The block obtained by moving left */
def left = if (isStanding) deltaCol(-2, -1)
else if (b1.row == b2.row) deltaCol(-1, -2)
else deltaCol(-1, -1)
/** The block obtained by moving right */
def right = if (isStanding) deltaCol(1, 2)
else if (b1.row == b2.row) deltaCol(2, 1)
else deltaCol(1, 1)
/** The block obtained by moving up */
def up = if (isStanding) deltaRow(-2, -1)
else if (b1.row == b2.row) deltaRow(-1, -1)
else deltaRow(-1, -2)
/** The block obtained by moving down */
def down = if (isStanding) deltaRow(1, 2)
else if (b1.row == b2.row) deltaRow(1, 1)
else deltaRow(2, 1)
/**
* Returns the list of blocks that can be obtained by moving
* the current block, together with the corresponding move.
*/
def neighbors: List[(Block, Move)] = (up, Up) ::
(down, Down) ::
(left, Left) ::
(right, Right) :: Nil
/**
* Returns the list of positions reachable from the current block
* which are inside the terrain.
*/
def legalNeighbors: List[(Block, Move)] = neighbors.filter(t => t._1.isLegal)
/**
* Returns `true` if the block is standing.
*/
def isStanding: Boolean = b1 == b2
/**
* Returns `true` if the block is entirely inside the terrain.
*/
def isLegal: Boolean = terrain(b1) && terrain(b2)
}
} |