stratego.cpp 8.88 KB
Newer Older
Sam Moore's avatar
Sam Moore committed
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
#include "common.h"

#include "stratego.h"

using namespace std;

/**
 * Static variables
 */
Board Board::theBoard(14,14);
//nothing, boulder, flag, spy, scout, miner, sergeant, lietenant, captain, major, colonel, general, marshal, bomb, error
char  Piece::tokens[] = {'.','+','F','y','s','n','S','L','c','m','C','G','M','B','?'};
int Piece::maxUnits[] = {0,0,1,1,8,5,4,4,4,3,2,1,1,6,0};



#ifdef GRAPHICS
	Piece::TextureManager Piece::textures;
#endif //GRAPHICS


#ifdef GRAPHICS

Piece::TextureManager::~TextureManager()
{
	Array<Texture*>::Iterator i(*this);
	while (i.Good())
	{
		delete (*i);
		++i;
	}
}

Texture & Piece::TextureManager::operator[](const LUint & at)
{
	while (Array<Texture*>::Size() <= at)
	{
		char buffer[BUFSIZ];
		sprintf(buffer, "images/piece%lu.bmp", Array<Texture*>::Size());
		Array<Texture*>::Add(new Texture(buffer, false));
		
	}
	return *(Array<Texture*>::operator[](at));
}
#endif //GRAPHICS

/**
 * Gets the type of a piece, based off a character token
 * @param fromToken - character identifying the piece
 * @returns The type of the piece
 */
Piece::Type Piece::GetType(char fromToken)
{
	for (int ii=0; ii <= (int)(Piece::BOMB); ++ii)
	{
		if (tokens[ii] == fromToken)
		{
			return Type(Piece::NOTHING + ii);
		}
	}
	return Piece::BOULDER;
}

/**
 * Construct a new, empty board
 * @param newWidth - the width of the board
 * @param newHeight - the height of the board
 */
Board::Board(int newWidth, int newHeight) : winner(Piece::NONE), width(newWidth), height(newHeight), board(NULL)
{
	board = new Piece**[width];
	for (int x=0; x < width; ++x)
	{
		board[x] = new Piece*[height];
		for (int y=0; y < height; ++y)
			board[x][y] = NULL;
	}
}

/**
 * Cleanup a board
 */
Board::~Board()
{
	for (int x=0; x < width; ++x)
	{
		for (int y=0; y < height; ++y)
			delete board[x][y];
		delete [] board[x];
	}
}

/**
 * Print textual representation of the board to a stream
 * @param stream - the stream to print information to
 * @param reveal - Pieces matching this colour will have their identify revealed, other pieces will be shown as '#' or '*' for RED or BLUE respectively.
 */
void Board::Print(FILE * stream, const Piece::Colour & reveal)
{
	for (int y=0; y < height; ++y)
	{
		for (int x=0; x < width; ++x)
		{
			Piece * piece = board[x][y];
			if (piece == NULL)
			{
				fprintf(stream, ".");
			}
			else if (piece->colour != Piece::NONE && (piece->colour == reveal || reveal == Piece::BOTH))
			{
				fprintf(stream, "%c", Piece::tokens[piece->type]);
			}
			else
			{
				switch (piece->colour)
				{
					case Piece::RED:
						fprintf(stream, "#");
						break;
					case Piece::BLUE:
						fprintf(stream, "*");
						break;
					case Piece::NONE:
						fprintf(stream, "+"); 
						break;
					case Piece::BOTH:
						fprintf(stream, "$"); //Should never see these!
						break;
				}
			}
		}
		fprintf(stream, "\n");
	}
	
}


#ifdef GRAPHICS
/**
 * Draw the board state to graphics
 * @param reveal - Pieces matching this colour will be revealed. All others will be shown as blank coloured squares.
 */
void Board::Draw(const Piece::Colour & reveal)
{
	if (!Graphics::Initialised())
	{
		Graphics::Initialise("Stratego", width*32, height*32);
	}

	Graphics::ClearScreen();
	
	for (int y=0; y < height; ++y)
	{
		for (int x=0; x < width; ++x)
		{
			Piece * piece = board[x][y];
			if (piece == NULL)
			{
				//Don't display anything

			}
			else if (piece->colour != Piece::NONE && (piece->colour == reveal || reveal == Piece::BOTH))
			{
				//Display the piece
				Piece::textures[(int)(piece->type)].DrawColour(x*32,y*32,0,1, Piece::GetGraphicsColour(piece->colour));
				
			}
			else
			{
				switch (piece->colour)
				{
					case Piece::RED:
						Piece::textures[(int)(Piece::BOULDER)].DrawColour(x*32,y*32,0,1, Piece::GetGraphicsColour(piece->colour));
						break;
					case Piece::BLUE:
						Piece::textures[(int)(Piece::BOULDER)].DrawColour(x*32,y*32,0,1, Piece::GetGraphicsColour(piece->colour));
						break;
					case Piece::NONE:
						Piece::textures[(int)(Piece::BOULDER)].DrawColour(x*32,y*32,0,1, Piece::GetGraphicsColour(piece->colour));
						break;
					case Piece::BOTH:
						Piece::textures[(int)(Piece::BOULDER)].DrawColour(x*32,y*32,0,1, Piece::GetGraphicsColour(piece->colour));
						break;
				}
			}
		}
		
	}
	Graphics::UpdateScreen();
	
}
#endif //GRAPHICS

/**
 * Adds a piece to the board
 * @param x - x-coord to place the piece at, starting at zero, must be less than board width
 * @param y - y-coord to place the piece at, starting at zero, must be less than board height
 * @param newType - the Type of the piece
 * @param newColour - the Colour of the piece
 * @returns true if and only if the piece could be successfully added.
 */
bool Board::AddPiece(int x, int y, const Piece::Type & newType, const Piece::Colour & newColour)
{
	if (board == NULL || x < 0 || y < 0 || x >= width || y >= width || board[x][y] != NULL)
		return false;

	Piece * piece = new Piece(newType, newColour);
	board[x][y] = piece;
	return true;
}

/**
 * Gets a pointer to a piece at a board location
 * UNUSED
 * @param x - x-coord of the piece
 * @param y - y-coord of the piece
 * @returns pointer to the piece, or NULL if the board location was empty
 * @throws error if board is null or coords are invalid
 */
Piece * Board::GetPiece(int x, int y)
{
	assert(board != NULL);
	assert(x >= 0 && x < width && y >= 0 && y < height);
	return board[x][y];
}

/**
 * Moves a piece at a specified position in the specified direction, handles combat if necessary
 * @param x - x-coord of the piece
 * @param y - y-coord of the piece
 * @param direction - Direction in which to move (UP, DOWN, LEFT or RIGHT)
 * @param colour - Colour which the piece must match for the move to be valid
 * @returns A MovementResult which indicates the result of the move - OK is good, VICTORY means that a flag was captured, anything else is an error
 */
235
MovementResult Board::MovePiece(int x, int y, const Direction & direction, int multiplier,const Piece::Colour & colour)
Sam Moore's avatar
Sam Moore committed
236
237
238
{
	if (board == NULL) 
	{
239
		return MovementResult(MovementResult::NO_BOARD);
Sam Moore's avatar
Sam Moore committed
240
241
242
	}
	if (!(x >= 0 && x < width && y >= 0 && y < height)) 
	{
243
		return MovementResult(MovementResult::INVALID_POSITION);
Sam Moore's avatar
Sam Moore committed
244
245
246
247
	}
	Piece * target = board[x][y];
	if (target == NULL) 
	{
248
		return MovementResult(MovementResult::NO_SELECTION);
Sam Moore's avatar
Sam Moore committed
249
250
251
	}
	if (!(colour == Piece::NONE || target->colour == colour)) 
	{
252
		return MovementResult(MovementResult::NOT_YOUR_UNIT);
Sam Moore's avatar
Sam Moore committed
253
254
255
	}
	if (target->type == Piece::FLAG || target->type == Piece::BOMB || target->type == Piece::BOULDER) 
	{
256
		return MovementResult(MovementResult::IMMOBILE_UNIT);
Sam Moore's avatar
Sam Moore committed
257
258
259
	}
	if (multiplier > 1 && target->type != Piece::SCOUT)
	{
260
		return MovementResult(MovementResult::INVALID_DIRECTION); //Can only move a scout multiple times.
Sam Moore's avatar
Sam Moore committed
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
	}
	int x2 = x; int y2 = y;

	for (int ii=0; ii < multiplier; ++ii)
	{
		switch (direction)
		{
			case UP:
				--y2;
				break;
			case DOWN:
				++y2;
				break;
			case LEFT:
				--x2;
				break;
			case RIGHT:
				++x2;
				break;
		}
		if (!(x2 >= 0 && x2 < width && y2 >= 0 && y2 < height)) 
		{
283
			return MovementResult(MovementResult::INVALID_DIRECTION);
Sam Moore's avatar
Sam Moore committed
284
285
286
		}
		if (ii < multiplier-1 && board[x2][y2] != NULL)
		{
287
			return MovementResult(MovementResult::POSITION_FULL);
Sam Moore's avatar
Sam Moore committed
288
289
290
291
292
293
294
295
296
297
		}
	}
	Piece * defender = board[x2][y2];
	if (defender == NULL)
	{
		board[x][y] = NULL;
		board[x2][y2] = target;
	}
	else if (defender->colour != target->colour)
	{
298
299
300
		Piece::Type defenderType = defender->type;
		Piece::Type attackerType = target->type;

Sam Moore's avatar
Sam Moore committed
301
302
		if (defender->colour == Piece::NONE) 
		{
303
			return MovementResult(MovementResult::POSITION_FULL);
Sam Moore's avatar
Sam Moore committed
304
305
306
307
		}
		if (defender->type == Piece::FLAG)
		{
			winner = target->colour;
308
			return MovementResult(MovementResult::VICTORY);
Sam Moore's avatar
Sam Moore committed
309
310
311
312
313
		}
		else if (defender->type == Piece::BOMB)
		{
			if (target->type == Piece::MINER)
			{
314

Sam Moore's avatar
Sam Moore committed
315
316
317
				delete defender;
				board[x][y] = NULL;
				board[x2][y2] = target;
318
				return MovementResult(MovementResult::KILLS, attackerType, defenderType);
Sam Moore's avatar
Sam Moore committed
319
320
321
322
323
324
325
			}
			else
			{
				delete defender;
				delete target;
				board[x][y] = NULL;
				board[x2][y2] = NULL;
326
				return MovementResult(MovementResult::BOTH_DIE, attackerType, defenderType);
Sam Moore's avatar
Sam Moore committed
327
328
329
330
331
332
333
			}
		}
		else if (defender->type == Piece::MARSHAL && target->type == Piece::SPY)
		{
			delete defender;
			board[x][y] = NULL;
			board[x2][y2] = target;
334
			return MovementResult(MovementResult::KILLS, attackerType, defenderType);
Sam Moore's avatar
Sam Moore committed
335
336
337
338
339
340
		}
		else if (target->operator > (*defender))
		{
			delete defender;
			board[x][y] = NULL;
			board[x2][y2] = target;
341
			return MovementResult(MovementResult::KILLS, attackerType, defenderType);
Sam Moore's avatar
Sam Moore committed
342
343
344
345
346
347
		}
		else if (target->operator==(*defender) && rand() % 2 == 0)
		{
			delete defender;
			board[x][y] = NULL;
			board[x2][y2] = target;	
348
			return MovementResult(MovementResult::KILLS, attackerType, defenderType);
Sam Moore's avatar
Sam Moore committed
349
350
351
352
353
		}
		else
		{
			delete target;
			board[x][y] = NULL;
354
			return MovementResult(MovementResult::DIES, attackerType, defenderType);
Sam Moore's avatar
Sam Moore committed
355
356
357
358
		}
	}
	else
	{
359
		return MovementResult(MovementResult::POSITION_FULL);
Sam Moore's avatar
Sam Moore committed
360
	}
361
	return MovementResult(MovementResult::OK);
Sam Moore's avatar
Sam Moore committed
362
363
364
365
}