So I was asked to do a quick 2048 game with C++ but using only stuff like arrays, structures, functions, pointers without using vectors, lambda and so on. The restrictions are here because it’s just a study exercise (or homework I should say).
I might comment out the following code if needed. This is what I’ve came up with so far:
#include <iostream> #include <ctime> #include <iomanip> #include <windows.h> #include <conio.h> using namespace std; enum colors { BLACK, BLUE, GREEN, CYAN, RED, PURPLE, YELLOW, GREY, LIGHTGREY, LIGHTBLUE, LIGHTGREEN, LIGHTCYAN, LIGHTRED, LIGHTPURPLE, LIGHTYELLOW, WHITE }; void setConsoleColor(int textColor, int bgColor) { SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), (textColor + (bgColor * 16))); } int randN(int start, int end) { return rand() % (end - start) + start; } int randN(int n1, int n2, int percent) { if ((rand() % 100) < percent) return n1; else return n2; } enum Direction { Left = 75, Right = 77, Up = 72, Down = 80 }; struct Cell { int x; int y; int value; Cell() { x = -1; y = -1; value = -1; } Cell(int x, int y, int value) { this->x = x; this->y = y; this->value = value; } void setValue(int value) { this->value = value; } void display() { cout << setw(4) << value; } bool isEmpty() { return value == 0; } bool isEqualTo(Cell cell) { return value == cell.value; } }; struct Game { Cell *cells; Cell *prevCells; int size; int score = 0; int highscore; int moves = 0; bool isMoved() { for (int i = 0; i < size * size; i++) { if (!cells[i].isEqualTo(prevCells[i])) { moves += 1; return true; } } return false; } void Doubled(int points) { score += points; } bool isCellsFull() { for (int i = 0; i < size * size; i++) { if (cells[i].isEmpty()) return false; } return true; } Cell &findCell(int x, int y) { for (int i = 0; i < size * size; i++) { if (cells[i].x == x && cells[i].y == y) return cells[i]; } return Cell(); } Cell &randEmptyCell() { if (isCellsFull()) return Cell(); int i; do { i = randN(0, size * size); } while (!cells[i].isEmpty()); return cells[i]; } void create(int size) { this->size = size; cells = new Cell[size * size]; prevCells = new Cell[size * size]; for (int x = 0, c = 0; x < size; x++) { for (int y = 0; y < size; y++, c++) { cells[c] = Cell(x, y, 0); } } } void display() { for (int i = 0, c = 0; i < size + 1; i++) { for (int j = 0; j < size; j++) cout << " ----"; cout << endl; if (i == size) break; for (int j = 0; j < size + 1; j++, c++) { cout << "|"; if (j == size) break; if (cells[c].isEmpty()) cout << setw(4) << " "; else { setConsoleColor(LIGHTGREEN, BLUE); cells[c].display(); setConsoleColor(GREY, BLACK); } } cout << endl; } cout << "Score: " << score << " Highscore: " << highscore << endl; cout << "Moves: " << moves << endl; } void handleKey() { copy(cells, cells + size * size, prevCells); _getch(); int ch = _getch(); switch (ch) { case Left: moveCells(Left); break; case Right: moveCells(Right); break; case Up: moveCells(Up); break; case Down: moveCells(Down); break; } } void moveCells(Direction dir) { int start = dir == Left || dir == Up ? 0 : size - 1; int end = start == 0 ? size : -1; for (int x = 0; x < size; x++) { for (int y = start; y != end; (start < end ? y++ : y--)) { Cell &c1 = dir == Left || dir == Right ? findCell(x, y) : findCell(y, x); if (c1.isEmpty()) continue; for (int k = y + (start == 0 ? -1 : 1); k != (end == -1 ? size : -1); (start < end ? k-- : k++)) { Cell &c2 = dir == Left || dir == Right ? findCell(x, k) : findCell(k, x); Cell &c3 = dir == Left ? findCell(x, k + 1) : dir == Right ? findCell(x, k - 1) : dir == Up ? findCell(k + 1, x) : findCell(k - 1, x); if (c1.isEqualTo(c2)) { c2.setValue(c2.value * 2); c1.setValue(0); Doubled(c2.value); } else if (!c2.isEmpty() && c3.isEmpty()) { c3.setValue(c1.value); c1.setValue(0); } else if (k == start && c2.isEmpty()) { c2.setValue(c1.value); c1.setValue(0); } } } } } void spawnCells(int amount) { for (int i = 0; i < amount; i++) { randEmptyCell().setValue(randN(2, 4, 85)); } } bool isOver() { if (!isCellsFull()) return false; for (int i = 0; i < size; i++) { for (int j = 0; j < size; j++) { Cell ¢er = findCell(i, j); Cell &right = findCell(i, j + 1); Cell &bottom = findCell(i + 1, j); if (center.isEqualTo(right) || center.isEqualTo(bottom)) return false; } return true; } } bool isWin() { for (int i = 0; i < size * size; i++) { if (cells[i].value == 2048) return true; } return false; } }; void main() { setlocale(LC_ALL, "rus"); srand(time(0)); bool playAgain = true; int highscore = 0; do { Game game; game.create(4); game.spawnCells(2); game.highscore = highscore; game.display(); do { game.handleKey(); if (game.isMoved()) game.spawnCells(1); if (game.score > highscore) game.highscore = game.score; system("cls"); game.display(); } while (!game.isWin() && !game.isOver()); if (game.isOver()) { setConsoleColor(LIGHTRED, BLACK); cout << "You won!" << endl; setConsoleColor(GREY, BLACK); } else if (game.isWin()) { setConsoleColor(LIGHTGREEN, BLACK); cout << "You lost!" << endl; setConsoleColor(GREY, BLACK); } cout << "Play again? y/n\n"; char ch; do { cin >> ch; if (ch != 'y' && ch != 'n') cout << "Incorrect!\n"; } while (ch != 'y' && ch != 'n'); switch (ch) { case 'y': highscore = game.score; system("cls"); break; case 'n': playAgain = false; break; } } while (playAgain); }
What do you think needs to be changed/simplified? Is there a way to increase readability? Also I would appreciate some general tips for writing console games like that.