# Chapter 3 – A Text-Based Console World

First, let’s focus on the game logic. For now I won’t start using pixel graphics and just use the console. Let’s create a little ASCII adventure to stretch our legs. Now keep in mind that the logic remains the same regardless of the interface I’m using. Hence the logic code I write for an ASCII Console Game can be pretty easily adapted to work with a Windows Graphics Game.

Here’s a rough working sketch of the game:

```
/////////////////////////////////////
// Simple ASCII Console World
// Date: 12 July 2012
/////////////////////////////////////

#include <cstdlib>
#include <windows.h>

#include <iostream>
#include <string>
#include <vector>

// Our Custom Namespace
namespace pro {

///////////////////////////////
// Just a typedef for 'char'
///////////////////////////////
typedef char GameTile;

///////////////////////////////
// Simple Point Class
// Holds the X and Y values
// of a 2D Object
//////////////////////////////
template< typename T > struct Point
{
Point(void) : X(0), Y(0) {}
Point(T x, T y) : X(x), Y(y) {}
T X;
T Y;
};

/////////////////////////////
// Simple GamePlayer Class
// For controlling the
// Main Character
/////////////////////////////
struct GamePlayer
{
Point< int > Position;
GameTile Tile;

GamePlayer(std::vector< std::string >& map);

void MoveLeft(std::vector< std::string >& map);
void MoveRight(std::vector< std::string >& map);
void MoveUp(std::vector< std::string >& map);
void MoveDown(std::vector< std::string >& map);

void Draw(void);
};

//////////////////////////////////////////
// Constructor
// Sets the Player's original position
// 1 is the code for a happy face
/////////////////////////////////////////
GamePlayer::GamePlayer(std::vector< std::string >& map)
: Position(2, 2), Tile(1)
{
if (map[Position.X][Position.Y] == ' ')
{
map[Position.X][Position.Y] = Tile;
}
else
{
std::cerr << "Player's Tile is Occupied" << std::endl;
}
}

//////////////////////////////////////////
// Moves the Player 1 Tile To the Left
/////////////////////////////////////////
void GamePlayer::MoveLeft(std::vector< std::string >& map)
{
// If the tile is empty
if (map[Position.Y][Position.X - 1] == ' ')
{
// Remove our player's tile from it's original position
map[Position.Y][Position.X] = ' ';

Position.X --;
// Fill it with our player's tile
map[Position.Y][Position.X] = Tile;
}
}

//////////////////////////////////////////
// Moves the Player 1 Tile To the Right
/////////////////////////////////////////
void GamePlayer::MoveRight(std::vector< std::string >& map)
{
// If the tile is empty
if (map[Position.Y][Position.X + 1] == ' ')
{
// Remove our player's tile from it's original position
map[Position.Y][Position.X] = ' ';

Position.X ++;
// Fill it with our player's tile
map[Position.Y][Position.X] = Tile;
}
}

//////////////////////////////////////////
// Moves the Player 1 Tile Upwards
/////////////////////////////////////////
void GamePlayer::MoveUp(std::vector< std::string >& map)
{
// If the tile is empty
if (map[Position.Y - 1][Position.X] == ' ')
{
// Remove our player's tile from it's original position
map[Position.Y][Position.X] = ' ';

Position.Y --;
// Fill it with our player's tile
map[Position.Y][Position.X] = Tile;
}
}

//////////////////////////////////////////
// Moves the Player 1 Tile Downwards
/////////////////////////////////////////
void GamePlayer::MoveDown(std::vector< std::string >& map)
{
// If the tile is empty
if (map[Position.Y + 1][Position.X] == ' ')
{
// Remove our player's tile from it's original position
map[Position.Y][Position.X] = ' ';

Position.Y ++;
// Fill it with our player's tile
map[Position.Y][Position.X] = Tile;
}
}

} // namespace pro

////////////////////////////////
// A Character Representation
// Of Our Tile Map
///////////////////////////////
static std::string tileMap[] = {

"==========",
"=        =",
"=        =",
"=        =",
"=        =",
"=        =",
"=        =",
"=========="
};

/** Not recommended, but works for simple stuff... **/

///////////////////////////////////////////////////
// Calculates the vertical length of the tile map
//////////////////////////////////////////////////
static int mapLenght = sizeof(tileMap) / sizeof(tileMap[0]);

///////////////////////////////////////////////////
// Calculates the horizontal width of the tile map
///////////////////////////////////////////////////
static int mapWidth = tileMap[0].size();

///////////////////////////////////////////////////////////////
// Vector, to prevent us from dealing with the lousy pointer
// arithmetic that comes with arrays
///////////////////////////////////////////////////////////////
static std::vector< std::string > tileVector(tileMap, tileMap + mapLenght);

//////////////
// The player
//////////////
static pro::GamePlayer player(tileVector);

////////////////////////
// Draws the Tile Map
///////////////////////
void Draw(void)
{
std::system("cls");

for (int y = 0; y < mapLenght; y++)
{
for (int x = 0; x < mapWidth; x++)
{
std::cout << tileVector[y][x];
}

std::cout << "\n";
}
}

//////////////////////////////////////
// Gets and Responds to user input
// from the keyboard
/////////////////////////////////////
bool GetInput(void)
{
if (GetAsyncKeyState(VK_UP))
{
player.MoveUp(tileVector);
}
else if (GetAsyncKeyState(VK_DOWN))
{
player.MoveDown(tileVector);
}
else if (GetAsyncKeyState(VK_LEFT))
{
player.MoveLeft(tileVector);
}
else if (GetAsyncKeyState(VK_RIGHT))
{
player.MoveRight(tileVector);
}
else
{
return false;
}
return true;
}

//////////////////////////////////////////
// Exit when the player presses 'Escape'
/////////////////////////////////////////
bool CheckExit()
{
if (GetAsyncKeyState(VK_ESCAPE))
{
return true;
}

return false;
}

//////////////////////////////////
// The main entrypoint function
/////////////////////////////////
int main()
{
bool open = true;

Draw();

/////////////////////////
// The Main Game Loop
////////////////////////
while (open)
{
////////////////////////////////////////////
// Redraw if the player's position changes
////////////////////////////////////////////
if (GetInput()) Draw();

if (CheckExit()) open = false;
}

return 0;
}

```

### Some Issues

• GetASyncKeyState() and system(“cls”) are not cross-platform, we may need to use something else
• We may need to make the code more comprehensive, object-oriented and improve the design
• We still need to code the player’s interaction with other tiles

### Writing Cross-platform Code

Although the above code will probably compile without any errors on most versions of Windows, there are a few things that may not work well on other operating systems such as Ubuntu etc. Firstly, I use a system call for clearing the screen. One solution to making the call cross-platform is to use pre-processor definitions to find out what platform we’re compiling on and use that system’s specific command for clearing the screen. For example:

```void clear_screen(void)
{
#if defined (WIN32) || defined (_WIN32)

system("cls"); // use the "cls" command on windows

#else

system("clear"); // otherwise use the unix "clear" command

#endif
}
```

Secondly, I use GetASyncKeyState() for input, which is a windows api function and hence is only supported on windows.

The best thing for writing cross-platform code is to use a third-party cross-platform library.