-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathmain.cpp
More file actions
349 lines (325 loc) · 10.4 KB
/
main.cpp
File metadata and controls
349 lines (325 loc) · 10.4 KB
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
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
#include <limits.h>
#include <iostream>
#include <time.h>
#include <stdio.h>
#include <stdlib.h>
#include <iomanip>
#include <ctype.h>
#include <string.h>
#define MERGE_MASK INT_MAX - 1
#define GRID_SIZE 4
#define TILE_COUNT GRID_SIZE *GRID_SIZE
#define INITAL_POPULATED_TILES 2
#define STARTING_VALUES_SIZE 2
using namespace std;
// direction flags
static const int INVALID = 0, LEFT = 1, UP = 2, RIGHT = 3, DOWN = 4;
static const int EMPTY = 0;
static const int MERGED_FLAG = 0x01;
static const int STARTING_VALUES[STARTING_VALUES_SIZE] = {2, 4};
//--------------------------------------------------------------------
// Display Instructions
void displayInstructions() {
cout << "Welcome to 1024. \n"
<< " \n"
<< "For each move enter a direction as a letter key, as follows: \n"
<< " W \n"
<< " A S D \n"
<< "where A=left,W=up, D=right and S=down. \n"
<< " \n"
<< "After a move, when two identical valued tiles come together they "
"\n"
<< "join to become a new single tile with the value of the sum of the "
"\n"
<< "two originals. This value gets added to the score. On each move "
"\n"
<< "one new randomly chosen value of 2 or 4 is placed in a random open "
"\n"
<< "square. User input of x exits the game. "
"\n"
<< "\n"
<< "Game ends when you reach 1024."
<< "\n"
<< "\n";
} // end displayInstructions()
/*
* Generates the next number to populate an empty tile with a starting value
*/
// *** Arvan: Use camelCase for variable and function names.
static int next_number() {
int selected_index = rand() % STARTING_VALUES_SIZE;
return STARTING_VALUES[selected_index];
}
/*
* Generates index of next empty tile in board
*/
// *** Arvan: Use camelCase for variable and function names.
static int empty_index(unsigned int *board, size_t board_size) {
int index;
do {
index = rand() % board_size;
} while (board[index] != 0);
return index;
}
/*
* Populates an empty tile with a starting value
*/
// *** Arvan: Use camelCase for variable and function names.
static void populate_random_empty_tile(unsigned int *board, size_t board_size) {
int pieceToPlace = next_number();
int index = empty_index(board, board_size);
board[index] = pieceToPlace;
}
/*
* Gets the direction from WASD character choice
*/
// *** Arvan: Use camelCase for variable and function names.
static u_int8_t get_direction(char letter) {
switch (letter) {
case 'A':
return LEFT;
case 'W':
return UP;
case 'D':
return RIGHT;
case 'S':
return DOWN;
default:
return INVALID;
}
}
static void display_board(unsigned int *board, size_t board_size) {
cout << "\n";
for (int row = 0; row < GRID_SIZE; row++) {
cout << " ";
for (int col = 0; col < GRID_SIZE; col++) {
int current =
row * GRID_SIZE + col; // 1-d index corresponding to row & col
cout << setw(
6); // set output field size to 6 (Requires #include <iomanip> )
// display '.' if board value is 0
if (board[current] == 0) {
cout << '.';
} else {
cout << board[current];
}
}
cout << "\n\n";
}
}
static void prepare_start_game(unsigned int *board, size_t board_size,
int populated_tiles) {
for (int i = 0; i < populated_tiles; i++) {
populate_random_empty_tile(board, board_size);
}
}
/**
* this function takes all merged values and resets to original value
*/
static void recalibrate(unsigned int *board, size_t board_size) {
for (int i = 0; i < board_size; i++) {
board[i] &= MERGE_MASK;
}
}
int one_dimension_index(int i, int j, int length_row) {
return i * length_row + j;
}
void rotate90Clockwise(unsigned int *board)
{
for (int i = 0; i < GRID_SIZE / 2; i++) {
for (int j = i; j < GRID_SIZE - i - 1; j++) {
int temp = board[one_dimension_index(i,j,GRID_SIZE)];
board[one_dimension_index(i,j,GRID_SIZE)] = board[one_dimension_index(GRID_SIZE - 1 - j,i,GRID_SIZE)];
board[one_dimension_index(GRID_SIZE - 1 - j,i, GRID_SIZE)] = board[one_dimension_index(GRID_SIZE - 1 - i,GRID_SIZE - 1 - j, GRID_SIZE)];
board[one_dimension_index(GRID_SIZE - 1 - i,GRID_SIZE - 1 - j, GRID_SIZE)] = board[one_dimension_index(j,GRID_SIZE - 1 - i, GRID_SIZE)];
board[one_dimension_index(j,GRID_SIZE - 1 - i, GRID_SIZE)] = temp;
}
}
}
static int move_pieces_left(unsigned int *board, size_t board_size) {
int score = 0;
for (int row = 0; row < GRID_SIZE; row++) {
int starting_row_index = row * GRID_SIZE;
int ending_row_index = starting_row_index + GRID_SIZE;
// loop through row
int empty_row_index = -1;
for (int i = starting_row_index; i < ending_row_index; i++) {
int tile_value = board[i];
// if slot empty in tile
if (tile_value == EMPTY) {
// keep index if previous value is not empty (bounds check)
if (i == starting_row_index) {
empty_row_index = i;
continue;
}
int prev_value = board[i - 1];
// they're both empty keep original empty row index
if (prev_value != tile_value) {
empty_row_index = i;
}
} else { // tile not empty
if (i == starting_row_index) {
continue;
}
// we have an empty value, merge with previous value
int value_index_to_check = i - 1;
if(empty_row_index > 0 && empty_row_index < i) {
value_index_to_check = empty_row_index-1;
}
int prev_value = board[value_index_to_check];
// if match - add to previous value
if (prev_value == tile_value) {
int updated_value = prev_value * 2;
board[value_index_to_check] = updated_value + 1;
score += updated_value;
board[i] = EMPTY;
empty_row_index = i;
} else {
if (empty_row_index == -1) {
continue;
}
board[empty_row_index] = tile_value;
board[i] = EMPTY;
i = empty_row_index;
}
}
}
}
recalibrate(board, board_size);
return score;
}
static int move_pieces_right(unsigned int *board, size_t board_size) {
int score = 0;
rotate90Clockwise(board);
rotate90Clockwise(board);
score += move_pieces_left(board, board_size);
rotate90Clockwise(board);
rotate90Clockwise(board);
return score;
}
static int move_pieces_top(unsigned int *board, size_t board_size) {
int score = 0;
rotate90Clockwise(board);
rotate90Clockwise(board);
rotate90Clockwise(board);
score += move_pieces_left(board, board_size);
rotate90Clockwise(board);
return score;
}
static int move_pieces_bottom(unsigned int *board, size_t board_size) {
int score = 0;
rotate90Clockwise(board);
score += move_pieces_left(board, board_size);
rotate90Clockwise(board);
rotate90Clockwise(board);
rotate90Clockwise(board);
return score;
}
//--------------------------------------------------------------------
// Make a copy of the board. This is used after an attempted move
// to see if the board actually changed.
void copyBoard(
unsigned int previousBoard[], // destination for board copy
unsigned int board[], // board from which copy will be made
int squaresPerSide) // size of the board
{
for( int row=0; row<squaresPerSide; row++) {
for( int col=0; col<squaresPerSide; col++ ) {
int current = row*squaresPerSide + col; // 1-d index corresponding to row & col
previousBoard[ current] = board[ current];
}
}
}//end copyBoard()
// 1 is valid still / -1 is invalid
int test_board(unsigned int *board) {
for(int i = 0; i < TILE_COUNT; i++) {
if(board[i] == 0) {
return 1;
}
}
unsigned int newBoard[TILE_COUNT];
copyBoard(newBoard,board,GRID_SIZE);
move_pieces_left(newBoard,TILE_COUNT);
move_pieces_bottom(newBoard,TILE_COUNT);
for(int i = 0; i < TILE_COUNT; i++){
if(board[i] != newBoard[i]) {
return 1;
}
}
for(int i = 0; i < TILE_COUNT; i++) {
if(board[i] = 1024) {
return 100;
}
}
return -1;
}
int main(void) {
// srand(time(NULL));
unsigned int game_board[TILE_COUNT] = {0};
int move_counter = 1, score = 0;
prepare_start_game(game_board, TILE_COUNT, INITAL_POPULATED_TILES);
displayInstructions();
printf("Score: %d\n\n", score);
display_board(game_board, TILE_COUNT);
char character_input;
cout << endl << move_counter << ". Your move: ";
cin >> character_input;
char normalized_input = toupper(character_input);
while (true) {
if(normalized_input == 'P') {
int replace_index = 0,
replaced_value = 0;
cin >> replace_index
>> replaced_value;
game_board[replace_index] = replaced_value;
printf("Score: %d\n", score);
display_board(game_board, TILE_COUNT);
// character_input = reask for value
cout << move_counter << ". Your move: ";
cin >> character_input;
normalized_input = toupper(character_input);
continue;
}
if(normalized_input == 'X') {
cout << endl << "Thanks for playing. Exiting program... \n\n";
break;
}
u_int8_t direction = get_direction(normalized_input);
if (direction == INVALID) {
cout << "Invalid input, please retry.";
} else {
switch (direction) {
case LEFT:
score += move_pieces_left(game_board, TILE_COUNT);
break;
case RIGHT:
score += move_pieces_right(game_board, TILE_COUNT);
break;
case DOWN:
score += move_pieces_bottom(game_board, TILE_COUNT);
break;
case UP:
score += move_pieces_top(game_board, TILE_COUNT);
break;
}
move_counter++;
if(test_board(game_board) == -1) {
cout << "\n"
<< "No more available moves. Game is over.\n"
<< "\n";
}
}
// check if full board
// if(new_number_position(game_board,TILE_COUNT) == -1) {
// printf("Board is full, 1024 not reached");
// }
populate_random_empty_tile(game_board, TILE_COUNT);
printf("Score: %d\n", score);
display_board(game_board, TILE_COUNT);
// character_input = reask for value
cout << move_counter << ". Your move: ";
cin >> character_input;
normalized_input = toupper(character_input);
}
return 0;
}