Code:
#include "Game.h"#include <curses.h>
#include <ctime>
int main() {
// char font[][][] used for pretty output
const char font[5][18][12] = {
{" "," # "," #### "," #### "," # # "," #### "," # "," #### "," #### "," #### "," # #### "," # # "," # #### "," # #### "," # # # "," # #### "," # # "," # #### "},
{" "," # "," # "," # "," # # "," # "," # "," # "," # # "," # # "," # # # "," # # "," # # "," # # "," # # # "," # # "," # # "," # # "},
{" "," # "," #### "," #### "," #### "," #### "," #### "," # "," #### "," #### "," # # # "," # # "," # #### "," # #### "," # #### "," # #### "," # #### "," # # "},
{" "," # "," # "," # "," # "," # "," # # "," # "," # # "," # "," # # # "," # # "," # # "," # # "," # # "," # # "," # # # "," # # "},
{" "," # "," #### "," #### "," # "," #### "," #### "," # "," #### "," # "," # #### "," # # "," # #### "," # #### "," # # "," # #### "," # #### "," # # "}};
Game test;
// initialize curses and all that.
initscr();
raw();
curs_set(0);
keypad(stdscr, TRUE);
noecho();
// if there is a game to load, load it, otherwise start new game.
int ch = (test.LoadGame()) ? ('n') : (0);
// catch various keys untill quit-
do {
switch(ch) {
case 'w':
case 'W':
case KEY_UP:
test.MoveUp();
break;
case 's':
case 'S':
case KEY_DOWN:
test.MoveDown();
break;
case 'a':
case 'A':
case KEY_LEFT:
test.MoveLeft();
break;
case 'd':
case 'D':
case KEY_RIGHT:
test.MoveRight();
break;
case 'n':
case 'N':
test.SetSeed(time(0));
test.NewGame();
break;
default:
{
}
}
// print the board.
mvprintw(0, 0, "+-----------+-----------+-----------+-----------+\n");
for(int y = 0; y < 4; y++) {
for(int n = 0; n < 5; n++)
printw("|%s|%s|%s|%s|\n", font[n][test.GetField(y, 0)], font[n][test.GetField(y, 1)], font[n][test.GetField(y, 2)], font[n][test.GetField(y, 3)]);
printw("+-----------+-----------+-----------+-----------+\n");
}
printw("Score: %d\n", test.GetScore());
printw("Moves: %d\n\n", test.GetMoves());
printw("Press'n' for new game and 'q' for quits.\nUse the arrow keys or 'a, s, d, w' to play.\n");
// get key;
ch = getch();
} while(ch != 'q' && ch != 'Q');
// end curses.
endwin();
// save game.
test.SaveGame();
return 0;
}
Game.cpp:
Code:
#include "Game.h"
Game::Game() {
// Allocate memory and initialize rotated boards.
for(int y = 0; y < 4; y++)
for(int x = 0; x < 4; x++)
board[3][3-x][y] = board[2][3-y][3-x] = board[1][x][3-y] = board[0][y][x] = new char;
}
Game::~Game() {
// Deallocate memory.
for(int y = 0;y < 4; y++)
for(int x = 0; x < 4; x++)
delete board[0][y][x];
}
void Game::NewGame() {
// Initialize random number generator from seed.
srand(seed);
// Initialize/reset various variables.
moves = score = rcount = 0;
// Reset board.
for(int y = 0; y < 4; y++)
for(int x = 0; x < 4; x++)
*board[0][y][x] = 0;
// Call for the initial tiles.
NewTile();
NewTile();
}
// Move board[z] left. Return 1 if not possible.
bool Game::Move(direction z) {
// Initialize b to 1 / Assume failure.
bool b = 1;
// Move all fields left where possible, due to empty squares.
// do not otherwise change anything.
for(int y = 0; y < 4; y++)
for(int n = 0; n < 3; n++)
for(int x = 2; x >= 0; x--)
if(!*board[z][y][x] && *board[z][y][x+1]) {
*board[z][y][x] = *board[z][y][x+1];
*board[z][y][x+1] = 0;
// if board has changes, success! Set b to 0.
b = 0;
}
// Row for Row.
for(int y = 0; y < 4; y++) {
// A whole lot of testing and moving about. Explanation difficult.
if(*board[z][y][0] && *board[z][y][0] == *board[z][y][1]) {
(*board[z][y][0])++;
score += P2(*board[z][y][0]);
if(*board[z][y][2] && *board[z][y][2] == *board[z][y][3]) {
*board[z][y][1] = *board[z][y][2]+1;
score += P2(*board[z][y][1]);
*board[z][y][2] = *board[z][y][3] = 0;
}
else {
*board[z][y][1] = *board[z][y][2];
*board[z][y][2] = *board[z][y][3];
*board[z][y][3] = 0;
}
// if board has changes, success! Set b to 0.
b = 0;
}
else if(*board[z][y][1] && *board[z][y][1] == *board[z][y][2]) {
(*board[z][y][1])++;
*board[z][y][2] = *board[z][y][3];
score += P2(*board[z][y][1]);
*board[z][y][3] = 0;
// if board has changes, success! Set b to 0.
b = 0;
}
else if(*board[z][y][2] && *board[z][y][2] == *board[z][y][3]) {
(*board[z][y][2])++;
score += P2(*board[z][y][2]);
*board[z][y][3] = 0;
// if board has changes, success! Set b to 0.
b = 0;
}
}
// if b == 0, Success! Call NewTile and increment moves.
if(!b) {
NewTile();
moves++;
}
// Return success or failure.
return b;
}
bool Game::NewTile() {
// Initialize b to 1 / Assume failure.
bool b = 1;
// Test for any empty squares.
for(int y = 0; y < 4; y++)
for(int x = 0; x < 4; x++)
if(!*board[0][y][x]) {
b = 0;
break;
}
// If no empty squares, failure. Return 1, else continue.
if(b)
return 1;
// Initialize c and generate random 1 or 2, with 90% likelyhood of 1.
char c = (rand() % 10)?(1):(2);
// Initialize bp and generate random integer from 0 to 15.
int bp = rand() % 16;
// increase rcount by the number of times rand() hase been called.
// The use for this will become apparent.
rcount += 2;
// While bp does not refar to an empty sqare, generate a new one.
while(*board[0][bp/4][bp%4]) {
bp = rand() % 16;
rcount++;
}
// Assign c to the the relevant square.
*board[0][bp/4][bp%4] = c;
// NB: Al this randomness could be done a whole lot better.
// Just haven't thought of a way yet.
}
// Avoid std::math.
// return 2^n
int Game::P2(char n) {
int result = 1;
for(; n > 0; n--)
result *= 2;
return result;
}
// Call Move() with the relevant directional parameter.
bool Game::MoveLeft() {
return Move(LEFT);
}
bool Game::MoveDown() {
return Move(DOWN);
}
bool Game::MoveRight() {
return Move(RIGHT);
}
bool Game::MoveUp() {
return Move(UP);
}
// Set a specific seed. Should be done prior to calling NewGame().
void Game::SetSeed(unsigned int n) {
seed = n;
}
// Return specific square.
int Game::GetField(unsigned int y, unsigned int x) {
return (y > 3 || x > 3) ? -1 : (int)*board[0][y][x];
}
// return score.
int Game::GetScore() {
return score;
}
// Return moves.
int Game::GetMoves() {
return moves;
}
// save game
bool Game::SaveGame() {
// Open file...
std::ofstream file("game.save");
// If file is open...
if (file.is_open()) {
// Save *board[0] in the form of a 16 letter string with values ranging from 'A' to 'R'
for(int y = 0; y < 4; y++)
for(int x = 0; x < 4; x++)
file << char(*board[0][y][x] + 65);
// Save seed, score, moves and rcount in the form of integer strings.
file << "\n" << seed;
file << "\n" << score;
file << "\n" << moves;
file << "\n" << rcount;
// Close file and return 0.
file.close();
return 0;
}
// Something went wrong. Return 1.
else
return 1;
}
bool Game::LoadGame() {
// We apparently need a string.
std::string line;
// As before. Open and test.
std::ifstream file("game.save");
if(file.is_open()) {
// Get first line...
getline(file, line);
// Get single letters, return them to their original value and position.
for(int y = 0; y < 4; y++)
for(int x = 0; x < 4; x++)
*board[0][y][x] = line.at(y*4+x) - 65;
// Get second line etc...
getline(file, line);
// Convert to int and return to original value and repeat...
seed = std::stoi(line);
getline(file, line);
score = std::stoi(line);
getline(file, line);
moves = std::stoi(line);
getline(file, line);
rcount = std::stoi(line);
// Close file.
file.close();
srand(seed);
// NB: There must be a better way...
// Call rand() the appropriate number of times to return to the original state.
for(int n = 0; n < rcount; n++)
rand();
// We did it! Return 0.
return 0;
}
// Something went wrong. Return 1.
else
return 1;
}
Game.h: