OLD | NEW |
| (Empty) |
1 | |
2 #include "hex_instance.h" | |
3 | |
4 #include <ctype.h> | |
5 #include <stdio.h> | |
6 #include <string.h> | |
7 | |
8 #include <iostream> | |
9 using std::cout; | |
10 using std::cin; | |
11 using std::endl; | |
12 | |
13 | |
14 namespace hexgame { | |
15 | |
16 // Hex Maniac v1.2 | |
17 // Copyright Cameron Browne | |
18 // 5/10/2000 | |
19 // | |
20 // Point-pairing algorithm with blind row. | |
21 // | |
22 // Feel free to experiment with and develop this code, but please | |
23 // acknowledge its contribution to any derived work. | |
24 // | |
25 // Compiles with the Microsoft C/C++ compiler (12.00.8168) and linker | |
26 // (6.00.8168) but should be portable. Use the following options: | |
27 // cl -GX hex_12.cpp | |
28 // | |
29 // Guaranteed bug free. Any errors must have been introduced by | |
30 // subsequent developers :) | |
31 // | |
32 // From: http://www.cameronius.com/games/hex/hex_12.cpp | |
33 // which is part of http://www.cameronius.com/games/hex/ | |
34 // | |
35 | |
36 | |
37 ////////////////////////////////////////////////////////////////////////////// | |
38 | |
39 const int MIN_N = 3; // Smallest board size | |
40 const int MAX_N = 26; // Largest board size (limit of alphanumeric notatio
n) | |
41 | |
42 enum State // board point or move state | |
43 { | |
44 EMPTY = 0, // empty board position | |
45 VERT = 1, // computer player (vertical = j) | |
46 HORZ = 2, // human player (horizontal = i) | |
47 NUM_STATES | |
48 }; | |
49 | |
50 static const char* StateNames[NUM_STATES] = | |
51 { | |
52 "Empty", | |
53 "Vertical", | |
54 "Horizontal", | |
55 }; | |
56 | |
57 struct Coord | |
58 { | |
59 Coord(int i_=-1, int j_=-1) : i(i_), j(j_) {} | |
60 | |
61 int i; | |
62 int j; | |
63 }; | |
64 | |
65 // Hexagonal neighbours of X: | |
66 // >-< | |
67 // >-< 0 >-< | 5 | 0 | |
68 // < 5 >-< 1 > ---+---+--- | |
69 // >-< X >-< == 4 | X | 1 | |
70 // < 4 >-< 2 > ---+---+--- | |
71 // >-< 3 >-< 3 | 2 | | |
72 // >-< | |
73 static const Coord nbor[6] = | |
74 { | |
75 Coord(1,-1), Coord(1,0), Coord(0,1), | |
76 Coord(-1,1), Coord(-1,0), Coord(0,-1) | |
77 }; | |
78 | |
79 void ConvertColRowToHexManiac(int column, int row, int *hex_i, int *hex_j); | |
80 void ConvertHexManiacToColRow(int hex_i, int hex_j, int* column, int* row); | |
81 | |
82 // Blind row defense templates | |
83 // | |
84 // Key: | |
85 // h = existing HORZ piece | |
86 // v = existing VERT piece | |
87 // . = empty position | |
88 // x = existing piece (VERT or HORZ) | |
89 // - = don't care | |
90 // H = last HORZ move | |
91 // V = VERT's best reply | |
92 // r = reentrant block on furthest connected piece (or adjacent if none) | |
93 // | |
94 // Note: more general situations should go last. | |
95 // | |
96 const int NUM_DEFENSE_TEMPLATES = 25; | |
97 static const char* HEX_DefenseTemplate[NUM_DEFENSE_TEMPLATES][2] = | |
98 { | |
99 { "H V - -", "- - - -" }, // acute blind corner | |
100 { "- - . H", "- - V -" }, // obtuse blind corner | |
101 { "- V H v", "- - - -" }, | |
102 { "- v H V", "- - - -" }, | |
103 { "- V H .", "h . . h" }, | |
104 { "- . H V", "h . . h" }, | |
105 { "v h H -", "V . h -" }, | |
106 { "- H h v", "h . V -" }, | |
107 { "- V H -", "h . h -" }, | |
108 { "- - H V", "- h . h" }, | |
109 { "- V H -", "- v - -" }, | |
110 { "- H V -", "- v - -" }, | |
111 { "- V H -", "- - h -" }, | |
112 { "- H V -", "h - - -" }, | |
113 { ". H h -", "V - - -" }, | |
114 { "- h H .", "- - V -" }, | |
115 { "- V H -", "h . - -" }, | |
116 { "- H V -", "- . h -" }, | |
117 { "- h H h", "- v V v" }, | |
118 { "- h H h", "v V v -" }, | |
119 { "- h H v", "- v V -" }, | |
120 { "- v H h", "- V v -" }, | |
121 { "- H r -", "h - - -" }, | |
122 { "- r H -", "- - h -" }, | |
123 { "- . H .", "- V . -" }, // reentrant block below (general case) | |
124 }; | |
125 | |
126 | |
127 /////////////////////////////////////////////////////////////////////////////// | |
128 | |
129 class HEX_Game | |
130 { | |
131 public: | |
132 HEX_Game(int n_=11) : N(n_), SwapOption(false) {} // HACKED SwapOption | |
133 ~HEX_Game() {} | |
134 | |
135 bool Play(void); | |
136 void SetInstance(HexGameInstance *instance) { instance_ = instance; } | |
137 private: | |
138 bool DoMove(State who); | |
139 bool GetUserMove(Coord& move) const; | |
140 bool GameWon(State who); | |
141 bool GameWon(State who, Coord const& from); | |
142 void RandomEmptyPosition(Coord& posn) const; | |
143 void ShowBoard(void) const; | |
144 bool IsValid(Coord const& posn) const; | |
145 | |
146 void BestOpening(Coord& move) const; | |
147 bool GoodSwap(Coord const& move) const; | |
148 bool BestComputerMove(Coord& move) const; | |
149 bool DefendBlindRow(Coord& move) const; | |
150 void SpareMove(Coord& move) const; | |
151 | |
152 void DoUpdate() const; | |
153 private: | |
154 State Board[MAX_N][MAX_N]; // the board | |
155 int N; // current board size NxN | |
156 int NumMoves; // number of moves played | |
157 Coord Last; // opponent's last move | |
158 bool SwapOption; // whether the swap option is enabled | |
159 bool HasSwapped; // whether a player has swapped already | |
160 State WhoStarted; // who played first | |
161 bool Visit[MAX_N][MAX_N]; // for rough working | |
162 | |
163 HexGameInstance* instance_; | |
164 }; | |
165 | |
166 | |
167 /////////////////////////////////////////////////////////////////////////////// | |
168 | |
169 | |
170 void HEX_Game::DoUpdate() const { | |
171 pp::Core* core = pp::Module::Get()->core(); | |
172 if(!core) | |
173 return; | |
174 printf("Got computer move, calling CallOnMainThread!\n"); | |
175 core->CallOnMainThread(0 /*no delay*/, pp::CompletionCallback( | |
176 UpdateCallback, instance_)); | |
177 } | |
178 | |
179 | |
180 // Alternates moves between players until the game is won | |
181 // | |
182 // Returns: whether user choose to continue playing | |
183 // | |
184 bool HEX_Game::Play(void) | |
185 { | |
186 memset(Board, 0, sizeof(Board)); | |
187 NumMoves = 0; | |
188 HasSwapped = false; | |
189 | |
190 // Determine who starts | |
191 cout << "\nDo you want to start? [y/n/q]: "; | |
192 char ch('n'); | |
193 | |
194 /// cin >> ch; | |
195 ch = 'y'; /// HACK -- allow user to start | |
196 | |
197 if (tolower(ch) == 'q') | |
198 exit(1); | |
199 | |
200 WhoStarted = (tolower(ch) == 'y') ? HORZ : VERT; | |
201 | |
202 if (WhoStarted == HORZ) | |
203 ShowBoard(); | |
204 | |
205 // Play a game | |
206 State who(WhoStarted); | |
207 while (!DoMove(who)) { | |
208 who = State((NumMoves + WhoStarted + 1) % 2 + 1); | |
209 } | |
210 | |
211 cout << "\nGame won by " << StateNames[who] << "!" << endl; | |
212 cout << "\nPlay again on " << N << 'x' << N << "? [y/n]: "; | |
213 cin >> ch; | |
214 | |
215 if (who == VERT) | |
216 instance_->SetComputerWins(); | |
217 else | |
218 instance_->SetUserWins(); | |
219 | |
220 DoUpdate(); | |
221 | |
222 return (tolower(ch) == 'y'); | |
223 } | |
224 | |
225 // Determines and plays a move for the specfied player | |
226 // | |
227 // Returns: whether this move wins the game | |
228 // | |
229 bool HEX_Game::DoMove(State who) | |
230 { | |
231 Coord move; | |
232 bool did_swap = (who==HORZ) ? GetUserMove(move) : BestComputerMove(move)
; | |
233 if (did_swap) | |
234 { | |
235 // 'who' chose to swap | |
236 State opp = (who == VERT) ? HORZ : VERT; | |
237 Board[Last.j][Last.i] = EMPTY; // undo opening move | |
238 Board[Last.i][Last.j] = who; // redo its reflection | |
239 ShowBoard(); | |
240 cout << endl << "Swap! " << StateNames[who][0] << " takes "; | |
241 cout << char('A' + Last.j) << 1 + Last.i << "." << endl; | |
242 | |
243 WhoStarted = who; | |
244 HasSwapped = true; | |
245 } | |
246 else | |
247 { | |
248 // Make the move | |
249 Board[move.j][move.i] = who; | |
250 NumMoves++; | |
251 Last = move; | |
252 | |
253 // Show the result | |
254 ShowBoard(); | |
255 cout << endl << StateNames[who][0] << " plays at "; | |
256 cout << char('A' + move.i) << 1 + move.j << "." << endl; | |
257 } | |
258 | |
259 return GameWon(who); | |
260 } | |
261 | |
262 // Reads user's move from the keyboard (HORZ) | |
263 // | |
264 // Returns: whether user chose to swap | |
265 // | |
266 // We call WaitForUserMove, which gets the user move from a queue (or waits | |
267 // for a new entry in the queue) in order to synchronize with the GUI. | |
268 // The indicated move could either be a replaces the cin code to get 'i/j'. | |
269 // Currently, we don't provide swap as an option. | |
270 // NOTE: We send an update (message) just once to indicate the game loop is | |
271 // ready, so that the UI knows it can now send user moves. | |
272 // | |
273 bool HEX_Game::GetUserMove(Coord& move) const | |
274 { | |
275 static bool gameInitialized = false; | |
276 if (!gameInitialized) { | |
277 // we need to send a message to the UI just once that we have | |
278 // reached this code -- which indicates that not only is the nexe | |
279 // loaded but that the game loop is ready for events to be queued. | |
280 instance_->SetGameLoopReady(); | |
281 DoUpdate(); | |
282 gameInitialized = true; | |
283 } | |
284 // char str[4]; | |
285 if (SwapOption && !HasSwapped && NumMoves==1) | |
286 { | |
287 char ch('y'); | |
288 cout << endl << "Swap opening move? "; | |
289 if (GoodSwap(Last)) | |
290 cout << "(I would) [y/n/q]: "; | |
291 else | |
292 cout << "(I wouldn't) [y/n/q]: "; | |
293 cin >> ch; | |
294 | |
295 if (tolower(ch) == 'q') | |
296 exit(2); | |
297 | |
298 if (tolower(ch) == 'y') | |
299 return true; // Swap | |
300 } | |
301 | |
302 do { | |
303 cout << endl << "Enter move [eg f6/q]: "; | |
304 cout << "Waiting for UI to send move..."; | |
305 // cin >> str; | |
306 | |
307 // if (str[0] == 'q') | |
308 // exit(3); | |
309 uint32_t column, row; | |
310 instance_->WaitForUserMove(&column, &row); | |
311 ConvertColRowToHexManiac(column, row, &move.i, &move.j); | |
312 printf("MOVE IS %d:%d\n", move.i, move.j); | |
313 //move.i = tolower(str[0]) - 'a'; | |
314 //move.j = atoi(&str[1]) - 1; | |
315 if (!IsValid(move)) { | |
316 cout << "MOVE " << move.i << ":" << move.j << " is NOT valid"
<< endl; | |
317 instance_->SetInvalidMove(); | |
318 DoUpdate(); | |
319 } else { | |
320 cout << "MOVE " << move.i << ":" << move.j << " is valid" << e
ndl; | |
321 instance_->SetValidMove(); | |
322 } | |
323 if (Board[move.j][move.i]) { | |
324 printf("Board non-empty contents for %d:%d are %d\n", move.i,
move.j, Board[move.j][move.i]); | |
325 } else { | |
326 printf("Board empty contents for %d:%d are %d\n", move.i, move
.j, Board[move.j][move.i]); | |
327 } | |
328 } while (!IsValid(move) || Board[move.j][move.i]); | |
329 | |
330 return false; | |
331 } | |
332 | |
333 // Returns: whether the game has been won by the specified player | |
334 // | |
335 bool HEX_Game::GameWon(State who) | |
336 { | |
337 fprintf(stderr, "Entered GameWon\n"); | |
338 memset(Visit, 0, sizeof(Visit)); | |
339 if (who == VERT) | |
340 { | |
341 for (int i = 0; i < N; i++) | |
342 if (Board[0][i]==VERT && !Visit[0][i] && GameWon(VERT, C
oord(i,0))) | |
343 return true; | |
344 } | |
345 else | |
346 { | |
347 for (int j = 0; j < N; j++) | |
348 if (Board[j][0]==HORZ && !Visit[j][0] && GameWon(HORZ, C
oord(0,j))) | |
349 return true; | |
350 } | |
351 fprintf(stderr, "Exited GameWon\n"); | |
352 return false; | |
353 } | |
354 | |
355 // Recursively examines neighbours of potential winning chains | |
356 // | |
357 // Returns: whether the game has been won by the specified player | |
358 // | |
359 bool HEX_Game::GameWon(State who, Coord const& from) | |
360 { | |
361 if ((who==HORZ && from.i==N-1) || (who==VERT && from.j==N-1)) | |
362 return true; | |
363 | |
364 Visit[from.j][from.i] = true; // this coord has been visited | |
365 | |
366 for (int n = 0; n < 6; n++) | |
367 { | |
368 // Check neighbours for continuation of chain | |
369 Coord to(from.i + nbor[n].i, from.j + nbor[n].j); | |
370 if | |
371 ( | |
372 IsValid(to) && Board[to.j][to.i]==who | |
373 && | |
374 !Visit[to.j][to.i] && GameWon(who, to) | |
375 ) | |
376 return true; | |
377 } | |
378 return false; | |
379 } | |
380 | |
381 // Draws board in ASCII text format | |
382 // | |
383 void HEX_Game::ShowBoard(void) const | |
384 { | |
385 // Show alphabetical (HORZ) labels | |
386 cout << endl << " "; | |
387 for (int i = 0; i < N; i++) | |
388 cout << char('A' + i) << ' '; | |
389 cout << endl; | |
390 | |
391 for (int j = 0; j < N; j++) | |
392 { | |
393 // Show numeric (VERT) labels | |
394 for (int s = 0; s < j; s++) | |
395 cout << ' '; | |
396 if (j < 9) | |
397 cout << ' '; | |
398 cout << j + 1; | |
399 | |
400 // Show state of each board position along this row | |
401 for (int i = 0; i < N; i++) | |
402 if (Board[j][i] == VERT) | |
403 cout << " V"; | |
404 else if (Board[j][i] == HORZ) | |
405 cout << " H"; | |
406 else | |
407 cout << " ."; | |
408 cout << endl; | |
409 } | |
410 } | |
411 | |
412 // Returns: whether posn is within the bounds of the board | |
413 // | |
414 bool | |
415 HEX_Game::IsValid(Coord const& posn) const | |
416 { | |
417 return (posn.i >= 0 && posn.i < N && posn.j >= 0 && posn.j < N); | |
418 } | |
419 | |
420 | |
421 /////////////////////////////////////////////////////////////////////////////// | |
422 // Strategic operations | |
423 | |
424 // Determines computer's best opening move (VERT) | |
425 // | |
426 void HEX_Game::BestOpening(Coord& move) const | |
427 { | |
428 move = Coord(N - 1, 0); // obtuse corner
in blind row | |
429 } | |
430 | |
431 // Returns: whether this opening move is worth swapping | |
432 // | |
433 bool | |
434 HEX_Game::GoodSwap(Coord const& move) const | |
435 { | |
436 return (move.i == N - move.j - 1); // swap along short diag
onal | |
437 } | |
438 | |
439 // Determines best move for computer (VERT) | |
440 // | |
441 // Returns: true on swap, else false | |
442 // | |
443 bool | |
444 HEX_Game::BestComputerMove(Coord& move) const | |
445 { | |
446 if (NumMoves == 0) | |
447 { | |
448 BestOpening(move); // opening move | |
449 } | |
450 else if (NumMoves == 1 && SwapOption && !HasSwapped && GoodSwap(Last)) | |
451 { | |
452 return true; // swap opponent
's opening move | |
453 } | |
454 else if (Last.j == 0) | |
455 { | |
456 DefendBlindRow(move); // defend the blind row | |
457 } | |
458 else | |
459 { | |
460 if (Last.i < N - Last.j) // determine dual point
on left | |
461 move = Coord(N - Last.j, N - Last.i - 1); | |
462 else // deter
mine dual point on right | |
463 move = Coord(N - Last.j - 1, N - Last.i); | |
464 } | |
465 | |
466 if (!IsValid(move) || Board[move.j][move.i]) | |
467 SpareMove(move); // spare move -
do some damage! | |
468 | |
469 | |
470 int ui_column, ui_row; | |
471 ConvertHexManiacToColRow(move.i, move.j, &ui_column, &ui_row); | |
472 // UPDATE COMPUTER'S MOVE | |
473 instance_->SetComputerMove(ui_column, ui_row); | |
474 DoUpdate(); | |
475 | |
476 return false; | |
477 } | |
478 | |
479 // Determines best move along the blind row (row 0) | |
480 // | |
481 // Returns: | |
482 // Whether appropriate defending move was found. | |
483 // | |
484 bool HEX_Game::DefendBlindRow(Coord& move) const | |
485 { | |
486 move = Coord(-1, -1); // should already be initialised but mak
e sure | |
487 | |
488 for (int d = 0; d < NUM_DEFENSE_TEMPLATES; d++) | |
489 { | |
490 // Find root position H on top row | |
491 int root; | |
492 for (root = 0; root < 4; root++) | |
493 if (HEX_DefenseTemplate[d][0][root * 2] == 'H') | |
494 break; | |
495 if | |
496 ( | |
497 (root>=4) // no ro
ot found | |
498 || | |
499 (root==0 && !(Last.i==0 && Last.j==0)) // acute corner
no good | |
500 || | |
501 (root==3 && !(Last.i==N-1 && Last.j==0)) // obtus
e corner no good | |
502 ) | |
503 continue; | |
504 | |
505 // Check whether rest of template matches board position | |
506 bool valid = true; | |
507 | |
508 int reentrant = 0; | |
509 | |
510 for (int j = 0; j < 2 && valid; j++) | |
511 for (int c = 0; c < 4 && valid; c++) | |
512 { | |
513 int i = Last.i - root + c; | |
514 char ch = HEX_DefenseTemplate[d][j][c * 2]; | |
515 | |
516 if (ch == 'V' && !Board[j][i])
| |
517 move = Coord(i, j); | |
518 else if (ch == 'V' && Board[j][i]) | |
519 valid = false; | |
520 else if (ch == 'h' && Board[j][i] != HORZ) | |
521 valid = false; | |
522 else if (ch == 'v' && Board[j][i] != VERT) | |
523 valid = false; | |
524 else if (ch == 'x' && !Board[j][i]) | |
525 valid = false; | |
526 else if (ch == '.' && Board[j][i]) | |
527 valid = false; | |
528 else if (ch == 'r') | |
529 { | |
530 if (Board[j][i] == VERT) | |
531 valid = false; | |
532 else | |
533 { | |
534 move = Coord(Last); | |
535 reentrant = (i < root) ? -1 : 1;
// is reentrant move | |
536 } | |
537 } | |
538 } | |
539 | |
540 if (!valid) | |
541 continue; // invalid template | |
542 | |
543 if (move.i == -1 || move.j == -1) | |
544 continue; // no valid reply found | |
545 | |
546 if (reentrant) | |
547 { | |
548 if (reentrant > 0) | |
549 { | |
550 // Find reentrant block above | |
551 while (move.i < N - 1) | |
552 { | |
553 if (!Board[0][move.i]) | |
554 return true;
// adjacent block above | |
555 | |
556 if (!Board[1][move.i]) | |
557 { | |
558 move = Coord(move.i, 1); | |
559 return true;
// reentrant block above | |
560 } | |
561 move.i++; | |
562 } | |
563 } | |
564 else | |
565 { | |
566 // Find reentrant block below | |
567 while (move.i >= 0) | |
568 { | |
569 if (!Board[0][move.i]) | |
570 return true;
// adjacent block above | |
571 | |
572 if (!Board[1][move.i - 1]) | |
573 { | |
574 move = Coord(move.i - 1, 1); | |
575 return true;
// reentrant block above | |
576 } | |
577 move.i--; | |
578 } | |
579 } | |
580 return false;
// no reentrant block found | |
581 } | |
582 return true; // good move found! | |
583 } | |
584 return false; | |
585 } | |
586 | |
587 | |
588 // Determines best spare move. There will ALWAYS be a spare move somewhere. | |
589 // | |
590 void HEX_Game::SpareMove(Coord& move) const | |
591 { | |
592 // Look for urgent moves on the blind row | |
593 move.j = 0; | |
594 for (move.i = 0; move.i < N; move.i++) | |
595 if | |
596 ( | |
597 !Board[0][move.i] | |
598 && | |
599 ( | |
600 (move.i < N - 1 && Board[0][move.i + 1] == HORZ
) | |
601 && | |
602 ( | |
603 (Board[1][move.i] == VERT ) | |
604 || | |
605 (move.i > 0 && !Board[1][move.i] && Boar
d[1][move.i-1]==HORZ) | |
606 ) | |
607 || | |
608 (move.i > 0 && Board[0][move.i - 1] == HORZ | |
609 && | |
610 ( | |
611 (Board[1][move.i - 1] == VERT ) | |
612 || | |
613 (!Board[1][move.i] && Board[1][move.i +
1] == HORZ ) | |
614 )) | |
615 ) | |
616 ) | |
617 return; | |
618 | |
619 // If top obtuse corner is empty, take it | |
620 if (!Board[0][N - 1]) | |
621 { | |
622 move = Coord(N - 1, 0); | |
623 return; | |
624 } | |
625 | |
626 // Block any reentrant point on row 1 | |
627 move.j = 1; | |
628 for (move.i = 0; move.i < N - 1; move.i++) // skip move.i =
= N - 1 | |
629 if | |
630 ( | |
631 !Board[1][move.i] | |
632 && | |
633 ( | |
634 (Board[0][move.i] == HORZ && Board[0][move.i + 1
] != HORZ ) | |
635 || | |
636 (Board[0][move.i + 1] == HORZ && Board[0][move.i
] != HORZ) | |
637 ) | |
638 ) | |
639 return; | |
640 | |
641 // Block any point adjacent to White on row 0 | |
642 move.j = 0; | |
643 for (move.i = 0; move.i < N; move.i++) | |
644 if | |
645 ( | |
646 !Board[0][move.i] | |
647 && | |
648 ( | |
649 (move.i > 0 && Board[0][move.i - 1] == HORZ ) | |
650 || | |
651 (move.i < N - 1 && Board[0][move.i + 1] == HORZ) | |
652 ) | |
653 ) | |
654 return; | |
655 | |
656 // Take any point an empty point away from Black on row 0 | |
657 move.j = 0; | |
658 for (move.i = 0; move.i < N; move.i++) | |
659 if | |
660 ( | |
661 !Board[0][move.i] | |
662 && | |
663 ( | |
664 (move.i > 1 && !Board[0][move.i-1] && Board[0][m
ove.i-2]==VERT ) | |
665 || | |
666 (move.i < N-2 && !Board[0][move.i+1] && Board[0]
[move.i+2]==VERT ) | |
667 ) | |
668 ) | |
669 return; | |
670 | |
671 // Take any empty point from top obtuse corner down (must be at least on
e) | |
672 for (move.j = 0; move.j < N; move.j++) | |
673 for (move.i = N-1; move.i >= 0; move.i--) | |
674 if (!Board[move.j][move.i]) | |
675 return; | |
676 | |
677 move = Coord(-1, -1); // bad result | |
678 } | |
679 | |
680 | |
681 /////////////////////////////////////////////////////////////////////////////// | |
682 | |
683 void* AppMain(void* param) | |
684 { | |
685 HexGameInstance* pepper_instance = static_cast<HexGameInstance*>(param); | |
686 cout << "Hex Maniac v1.2" << endl; | |
687 cout << "Cameron Browne 5/10/2000" << endl << endl; | |
688 cout << "You are horizontal (H), computer is vertical (V)" << endl; | |
689 | |
690 int n = 11; | |
691 /* | |
692 if (argc == 2) | |
693 { | |
694 n = atoi(argv[1]); | |
695 if (n < MIN_N) | |
696 n = MIN_N; | |
697 else if (n > MAX_N) | |
698 n = MAX_N; | |
699 } | |
700 */ | |
701 HEX_Game game(n); | |
702 game.SetInstance(pepper_instance); | |
703 while (game.Play()); | |
704 return 0; | |
705 } | |
706 | |
707 | |
708 | |
709 | |
710 // Move from JS is based on 'straight' columns that are 1-based | |
711 // 4:1 | |
712 // 2:1 3:1 4:2 | |
713 // 1:1 2:2 3:2 4:3 | |
714 // 3:3 4:4 | |
715 // | |
716 // while the i/j are based on 0-based grid that looks like: | |
717 // . . . . . . . . . . . | |
718 // . . . . . . . . . . . | |
719 // . . . . . . . . . . . | |
720 // . . . . . . . . . . . | |
721 // . . . . . . . . . . . | |
722 // . . . . . . . . . . . | |
723 // . . . . . . . . . . . | |
724 // | |
725 // where i is how many columns from the left we are (0-based) in this | |
726 // 'shifted' view | |
727 // | |
728 // Here's another view with the numbers from UI col:row shown | |
729 // | |
730 // 1:1 2:1 3:1 4:1 5:1 6:1 | |
731 // 2:2 3:2 4:2 5:2 | |
732 // 3:3 4:3 5:3 | |
733 // | |
734 // Top 1:1->0:0 2:1->1:0 3:1->2:0 4:1->3:0 | |
735 // Next 2:2->0:1 3:2->1:1 4:2->2:1 | |
736 // 3:3->0:2 4:3->1:3 | |
737 // | |
738 // Need to handle the case where the rhombus is shrinking -- column > 11 | |
739 // TODO (make sure this can be expanded to handle different sizes) | |
740 // Note that when column > 11, column is always > row | |
741 // 12:1 -> 10:1 | |
742 // 13:1 -> 10:2 | |
743 // 14:1 -> 10:3 | |
744 // | |
745 // 13:1 -> 10:2 | |
746 // 13:2 -> 9:3 | |
747 // 13:3 -> 8:4 | |
748 // | |
749 // col:row x:y col-11 - row | |
750 // 14:1 -> 10:3 3 | |
751 // 14:2 -> 9:4 3 | |
752 // 14:3 -> 8:5 3 | |
753 // | |
754 // 15:1 -> 10:4 | |
755 // 16:1 -> 10:5 | |
756 // 21:1 -> 10:10 | |
757 // 19:3 -> 8:10 | |
758 // | |
759 void ConvertColRowToHexManiac(int column, int row, int *hex_i, int *hex_j) { | |
760 if (column < 12) { // FIXME -- magic number (12) | |
761 *hex_i = column - row; | |
762 *hex_j = row -1; | |
763 } else { | |
764 *hex_j = (column-11) + (row - 1); | |
765 *hex_i = column - *hex_j - 1; | |
766 } | |
767 std::cout << " ConvertColRowToHexManiac column=" << column << " row=" << row | |
768 << " ... hex_i = " << *hex_i << " hex_j = " << *hex_j << std::endl; | |
769 if (*hex_i < 0 || *hex_i > 11 || *hex_j < 0 || *hex_j > 11) { | |
770 fprintf(stderr, "ERROR! BAD hex value!\n"); | |
771 } | |
772 } | |
773 | |
774 // Does the inverse of ConvertColRowToHexManiac | |
775 // Input of 10:1 should be 12:1 | |
776 void ConvertHexManiacToColRow(int hex_i, int hex_j, int* column, int* row) { | |
777 fprintf(stderr, "ENTERED ConvertHexManiacToColRow %d %d\n", hex_i, hex_j); | |
778 if (hex_i + hex_j >= 11) { // FIXME -- magic number (11) | |
779 *column = hex_i + hex_j + 1; | |
780 *row = hex_j - *column + 11 + 1; | |
781 } else { | |
782 // input of hex 1:8 => 10:9 | |
783 *row = hex_j + 1; | |
784 *column = hex_i + *row; | |
785 } | |
786 fprintf(stderr, " ... done conversion"); | |
787 fprintf(stderr, " ... result is %d:%d\n", *column, *row); | |
788 std::cout << " ConvertHexManiacToColRow " << hex_i << ":" << hex_j | |
789 << " ... " << *column << ":" << *row << std::endl; | |
790 } | |
791 | |
792 | |
793 } // empty namepsace | |
OLD | NEW |