-
Notifications
You must be signed in to change notification settings - Fork 4.3k
/
Copy pathMove.cs
278 lines (254 loc) · 9.13 KB
/
Move.cs
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
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
using System;
using UnityEngine;
namespace Unity.MLAgents.Integrations.Match3
{
/// <summary>
/// Directions for a Move.
/// </summary>
public enum Direction
{
/// <summary>
/// Move up (increasing row direction).
/// </summary>
Up,
/// <summary>
/// Move down (decreasing row direction).
/// </summary>
Down, // -row direction
/// <summary>
/// Move left (decreasing column direction).
/// </summary>
Left, // -column direction
/// <summary>
/// Move right (increasing column direction).
/// </summary>
Right, // +column direction
}
/// <summary>
/// Struct that encapsulates a swap of adjacent cells.
/// A Move can be constructed from either a starting row, column, and direction,
/// or from a "move index" between 0 and NumPotentialMoves()-1.
/// Moves are enumerated as the internal edges of the game grid.
/// Left/right moves come first. There are (maxCols - 1) * maxRows of these.
/// Up/down moves are next. There are (maxRows - 1) * maxCols of these.
/// </summary>
public struct Move
{
/// <summary>
/// Index of the move, from 0 to NumPotentialMoves-1.
/// </summary>
public int MoveIndex;
/// <summary>
/// Row of the cell that will be moved.
/// </summary>
public int Row;
/// <summary>
/// Column of the cell that will be moved.
/// </summary>
public int Column;
/// <summary>
/// Direction that the cell will be moved.
/// </summary>
public Direction Direction;
/// <summary>
/// Construct a Move from its move index and the board size.
/// This is useful for iterating through all the Moves on a board, or constructing
/// the Move corresponding to an Agent decision.
/// </summary>
/// <param name="moveIndex">Must be between 0 and NumPotentialMoves(maxRows, maxCols).</param>
/// <param name="maxBoardSize"></param>
/// <returns></returns>
/// <exception cref="ArgumentOutOfRangeException"></exception>
public static Move FromMoveIndex(int moveIndex, BoardSize maxBoardSize)
{
var maxRows = maxBoardSize.Rows;
var maxCols = maxBoardSize.Columns;
if (moveIndex < 0 || moveIndex >= NumPotentialMoves(maxBoardSize))
{
throw new ArgumentOutOfRangeException("moveIndex");
}
Direction dir;
int row, col;
if (moveIndex < (maxCols - 1) * maxRows)
{
dir = Direction.Right;
col = moveIndex % (maxCols - 1);
row = moveIndex / (maxCols - 1);
}
else
{
dir = Direction.Up;
var offset = moveIndex - (maxCols - 1) * maxRows;
col = offset % maxCols;
row = offset / maxCols;
}
return new Move
{
MoveIndex = moveIndex,
Direction = dir,
Row = row,
Column = col
};
}
/// <summary>
/// Increment the Move to the next MoveIndex, and update the Row, Column, and Direction accordingly.
/// </summary>
/// <param name="maxBoardSize"></param>
public void Next(BoardSize maxBoardSize)
{
var maxRows = maxBoardSize.Rows;
var maxCols = maxBoardSize.Columns;
var switchoverIndex = (maxCols - 1) * maxRows;
MoveIndex++;
if (MoveIndex < switchoverIndex)
{
Column++;
if (Column == maxCols - 1)
{
Row++;
Column = 0;
}
}
else if (MoveIndex == switchoverIndex)
{
// switch from moving right to moving up
Row = 0;
Column = 0;
Direction = Direction.Up;
}
else
{
Column++;
if (Column == maxCols)
{
Row++;
Column = 0;
}
}
}
/// <summary>
/// Construct a Move from the row, column, direction, and board size.
/// </summary>
/// <param name="row"></param>
/// <param name="col"></param>
/// <param name="dir"></param>
/// <param name="maxBoardSize"></param>
/// <returns></returns>
public static Move FromPositionAndDirection(int row, int col, Direction dir, BoardSize maxBoardSize)
{
// Check for out-of-bounds
if (row < 0 || row >= maxBoardSize.Rows)
{
throw new IndexOutOfRangeException($"row was {row}, but must be between 0 and {maxBoardSize.Rows - 1}.");
}
if (col < 0 || col >= maxBoardSize.Columns)
{
throw new IndexOutOfRangeException($"col was {col}, but must be between 0 and {maxBoardSize.Columns - 1}.");
}
// Check moves that would go out of bounds e.g. col == 0 and dir == Left
if (
row == 0 && dir == Direction.Down ||
row == maxBoardSize.Rows - 1 && dir == Direction.Up ||
col == 0 && dir == Direction.Left ||
col == maxBoardSize.Columns - 1 && dir == Direction.Right
)
{
throw new IndexOutOfRangeException($"Cannot move cell at row={row} col={col} in Direction={dir}");
}
// Normalize - only consider Right and Up
if (dir == Direction.Left)
{
dir = Direction.Right;
col = col - 1;
}
else if (dir == Direction.Down)
{
dir = Direction.Up;
row = row - 1;
}
int moveIndex;
if (dir == Direction.Right)
{
moveIndex = col + row * (maxBoardSize.Columns - 1);
}
else
{
var offset = (maxBoardSize.Columns - 1) * maxBoardSize.Rows;
moveIndex = offset + col + row * maxBoardSize.Columns;
}
return new Move
{
Row = row,
Column = col,
Direction = dir,
MoveIndex = moveIndex,
};
}
/// <summary>
/// Check if the move is valid for the given board size.
/// This will be passed the return value from AbstractBoard.GetCurrentBoardSize().
/// </summary>
/// <param name="boardSize"></param>
/// <returns></returns>
public bool InRangeForBoard(BoardSize boardSize)
{
var (otherRow, otherCol) = OtherCell();
// Get the maximum row and column this move would affect.
var maxMoveRow = Mathf.Max(Row, otherRow);
var maxMoveCol = Mathf.Max(Column, otherCol);
return maxMoveRow < boardSize.Rows && maxMoveCol < boardSize.Columns;
}
/// <summary>
/// Get the other row and column that correspond to this move.
/// </summary>
/// <returns></returns>
/// <exception cref="ArgumentOutOfRangeException"></exception>
public (int Row, int Column) OtherCell()
{
switch (Direction)
{
case Direction.Up:
return (Row + 1, Column);
case Direction.Down:
return (Row - 1, Column);
case Direction.Left:
return (Row, Column - 1);
case Direction.Right:
return (Row, Column + 1);
default:
throw new ArgumentOutOfRangeException();
}
}
/// <summary>
/// Get the opposite direction of this move.
/// </summary>
/// <returns></returns>
/// <exception cref="ArgumentOutOfRangeException"></exception>
public Direction OtherDirection()
{
switch (Direction)
{
case Direction.Up:
return Direction.Down;
case Direction.Down:
return Direction.Up;
case Direction.Left:
return Direction.Right;
case Direction.Right:
return Direction.Left;
default:
throw new ArgumentOutOfRangeException();
}
}
/// <summary>
/// Return the number of potential moves for a board of the given size.
/// This is equivalent to the number of internal edges in the board.
/// </summary>
/// <param name="maxBoardSize"></param>
/// <returns></returns>
public static int NumPotentialMoves(BoardSize maxBoardSize)
{
return maxBoardSize.Rows * (maxBoardSize.Columns - 1) + (maxBoardSize.Rows - 1) * (maxBoardSize.Columns);
}
}
}