1 /* 2 Copyright (C) 2005 Dan Alcantara (dfalcantara@ucdavis.edu, http://wwwcsif.cs.ucdavis.edu/~alcantar) 3 Based on the board game "Blokus" by Educational Insights, Inc. 4 5 This program is free software; you can redistribute 6 it and/or modify it under the terms of the GNU General 7 Public License as published by the Free Software Foundation; 8 either version 2 of the License, or (at your option) any later version. 9 10 This program is distributed in the hope that it will be useful, but 11 WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 12 or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 13 more details. 14 15 You should have received a copy of the GNU General Public License along 16 with this program; if not, write to the Free Software Foundation, Inc., 17 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 18 19 Full license visible at http://www.opensource.org/licenses/gpl-license.php 20 */ 21 22 #include <iostream> 23 #include <fstream> 24 25 #include <wx/wx.h> 26 #include <wx/glcanvas.h> 27 #include <wx/colordlg.h> 28 #include <wx/filedlg.h> 29 #include <wx/utils.h> 30 31 #include "game.h" 32 #include "options.h" 33 #include "help.h" 34 #include "docs.h" 35 #include "blokish.xpm" 36 37 #ifndef WIN32 38 #include <sys/types.h> 39 #include <pwd.h> 40 #endif 41 42 enum 43 { 44 ID_NEW_GAME = 100, 45 ID_CHANGE_FIRST, 46 ID_CHANGE_SECOND, 47 ID_CHANGE_THIRD, 48 ID_CHANGE_FOURTH, 49 ID_LOAD_GAME, 50 ID_SAVE_GAME, 51 ID_CHANGE_AI, 52 ID_SHOW_HELP, 53 ID_SHOW_ABOUT 54 }; 55 56 class BlokishAIThread:public wxThread 57 { 58 public: BlokishAIThread(BlokishGame * game)59 BlokishAIThread(BlokishGame *game):wxThread(wxTHREAD_JOINABLE) 60 { 61 mpGame = game; 62 mpGame->SetOutcome(BlokishGame::WaitForAI); 63 Create(); 64 } 65 Entry()66 virtual ExitCode Entry() 67 { 68 mpGame->ResetAI(); 69 mpGame->TakeAIStep(); 70 return 0; 71 } 72 73 protected: 74 BlokishGame *mpGame; 75 }; 76 BlokishAIThread *gAIThread; 77 bool gbAllowNewThreadToStart = true;; 78 79 class BlokishCanvas:public wxGLCanvas 80 { 81 struct Color 82 { ColorBlokishCanvas::Color83 Color(float r = 0, float g = 0, float b = 0, float a = 0) 84 { 85 values[0] = r; 86 values[1] = g; 87 values[2] = b; 88 values[3] = a; 89 } 90 operator []BlokishCanvas::Color91 float& operator[](int which) 92 { 93 return values[which]; 94 } 95 96 float values[4]; 97 }; 98 99 public: BlokishCanvas(wxWindow * parent,wxWindowID id,wxPoint pos,wxSize size,int attribList[],BlokishGame & game)100 BlokishCanvas(wxWindow *parent, wxWindowID id, wxPoint pos, wxSize size, int attribList[], BlokishGame &game) 101 :wxGLCanvas(parent, id, pos, size, 0, wxString::FromAscii(""), attribList), mpGame(&game) 102 { 103 mnNumPiecesPerRow = 7; 104 105 mvColor.clear(); 106 mvColor.reserve(4); 107 108 #ifdef WIN32 109 std::ifstream settingsFile(".blokishrc", std::ios::binary); 110 #else 111 wxString filename = wxGetHomeDir(); 112 filename += wxString::FromAscii("/.blokishrc"); 113 std::ifstream settingsFile(filename.fn_str(), std::ios::binary); 114 #endif 115 116 if(false == settingsFile.is_open()) 117 { 118 mvColor.push_back(Color(0.25, 0.25, 1.00)); 119 mvColor.push_back(Color(1.00, 1.00, 0.25)); 120 mvColor.push_back(Color(1.00, 0.25, 0.25)); 121 mvColor.push_back(Color(0.25, 1.00, 0.25)); 122 } 123 else 124 { 125 AI::AIOptions *options = mpGame->GetOptions(); 126 127 for(unsigned int nPlayer = 0; nPlayer < 4; nPlayer++) 128 { 129 float color[3]; 130 for(unsigned int nColor = 0; nColor < 3; nColor++) 131 { 132 settingsFile.read((char*) &(color[nColor]), sizeof(color[nColor])); 133 } 134 mvColor.push_back(Color(color[0], color[1], color[2])); 135 136 settingsFile.read((char*) &(options[nPlayer]), sizeof(options[nPlayer])); 137 138 } 139 } 140 } 141 WriteSettings()142 void WriteSettings() 143 { 144 AI::AIOptions *options = mpGame->GetOptions(); 145 146 #ifdef WIN32 147 std::cout << "Saving to .blokishrc.\n"; 148 std::ofstream settingsFile(".blokishrc", std::ios::binary); 149 #else 150 wxString filename = wxGetHomeDir(); 151 filename += wxString::FromAscii("/.blokishrc"); 152 std::ofstream settingsFile(filename.fn_str(), std::ios::binary); 153 154 if(false == settingsFile.is_open()) 155 { 156 std::cerr << "Failed to open file for writing.\n"; 157 exit(1); 158 } 159 #endif 160 161 for(unsigned int nPlayer = 0; nPlayer < 4; nPlayer++) 162 { 163 for(unsigned int nColor = 0; nColor < 3; nColor++) 164 { 165 settingsFile.write((char *) &(mvColor[nPlayer][nColor]), sizeof(mvColor[nPlayer][nColor])); 166 } 167 168 settingsFile.write((char*) &(options[nPlayer]), sizeof(options[nPlayer])); 169 } 170 } 171 OnTimer()172 void OnTimer() 173 { 174 if(gbAllowNewThreadToStart && mpGame->GetOutcome() == BlokishGame::BeginAIThread) 175 { 176 gAIThread = new BlokishAIThread(mpGame); 177 gAIThread->Run(); 178 179 Refresh(false); 180 Update(); 181 } 182 else if(mpGame->GetOutcome() == BlokishGame::WaitForAI) 183 { 184 Refresh(false); 185 } 186 else if(mpGame->GetOutcome() == BlokishGame::FinishAIThread) 187 { 188 if(gAIThread != NULL) 189 { 190 if(gAIThread->IsRunning()) 191 { 192 gAIThread->Delete(); 193 } 194 delete gAIThread; 195 gAIThread = NULL; 196 } 197 198 if(mpGame->FinishTurn() == BlokishGame::GameOver) 199 { 200 wxMessageBox(wxString::FromAscii("Game over!")); 201 } 202 203 Refresh(false); 204 Update(); 205 } 206 else if(mpGame->GetOutcome() == BlokishGame::GetPlayerInput) 207 { 208 Refresh(false); 209 } 210 } 211 OnDraw(wxPaintEvent & event)212 void OnDraw(wxPaintEvent &event) 213 { 214 wxPaintDC(this); 215 SetCurrent(); 216 217 glEnable(GL_BLEND); 218 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); 219 220 wxSize size = GetClientSize(); 221 222 // Reset 223 glViewport(0, 0, size.GetWidth() / 2, size.GetHeight()); 224 glMatrixMode(GL_PROJECTION); 225 glLoadIdentity(); 226 glOrtho(-.01, mpGame->GetNumCols()+.01, -.01, mpGame->GetNumRows()+.01, -1, 1); 227 228 glClear(GL_COLOR_BUFFER_BIT); 229 230 // Draw the board 231 glBegin(GL_QUADS); 232 glColor4f(1, 1, 1, 0.9); 233 glVertex2f(0, 0); 234 glColor4f(1, 1, 1, 0.95); 235 glVertex2f(0, mpGame->GetNumRows()); 236 glColor4f(1, 1, 1, 1.0); 237 glVertex2f(mpGame->GetNumCols(), mpGame->GetNumRows()); 238 glColor4f(1, 1, 1, 0.95); 239 glVertex2f(mpGame->GetNumCols(), 0); 240 glEnd(); 241 242 glBegin(GL_LINES); 243 glColor4f(0, 0, 0, 1); 244 glVertex2f(mpGame->GetNumCols(), 0); 245 glVertex2f(mpGame->GetNumCols(), mpGame->GetNumRows()); 246 glEnd(); 247 248 float round = mpGame->GetRound(); 249 250 for(int nRow = int(mpGame->GetNumRows()-1); nRow >= 0; nRow--) 251 { 252 for(int nCol = 0; nCol < mpGame->GetNumCols(); nCol++) 253 { 254 glPushMatrix(); 255 glTranslatef(nCol, nRow, 0); 256 257 int age = int(round) - mpGame->GetAge(nRow, nCol); 258 259 float factor = 0.0; 260 if(age < 4) 261 { 262 age = 4 - age; 263 factor = age / 4.0; 264 } 265 266 DrawSquare(mpGame->GetSpaceID(nRow, nCol), 1.0f); 267 268 // Draws dots on the more recent pieces. 269 if(mpGame->GetSpaceID(nRow, nCol) < MAX_PLAYER_ID && factor != 0.0f) 270 DrawSquare(usable, factor, false); 271 272 // Draws dots where pieces could fit 273 if(mpGame->IsUsableSpace(nRow, nCol)) 274 { 275 DrawSquare(usable, 1.0, false); 276 } 277 278 glPopMatrix(); 279 } 280 } 281 282 glViewport(size.GetWidth() / 2, 0, size.GetWidth() / 2, size.GetHeight()); 283 glLoadIdentity(); 284 glOrtho(0, mnNumPiecesPerRow*6, 0, mnNumPiecesPerRow*6, -1, 1); 285 286 glBegin(GL_QUADS); 287 glColor4f(.75, .75, .75, 1); 288 glVertex2f(0, 0); 289 glColor4f(.875, .875, .875, 1); 290 glVertex2f(0, mnNumPiecesPerRow*6); 291 glColor4f(1, 1, 1, 1); 292 glVertex2f(mnNumPiecesPerRow*6, mnNumPiecesPerRow*6); 293 glColor4f(.875, .875, .875, 1); 294 glVertex2f(mnNumPiecesPerRow*6, 0); 295 glEnd(); 296 297 // Draw the unused pieces 298 if(false == mpGame->IsGameOver()) 299 { // Draw the current player's unused pieces 300 DrawUnusedPlayerPieces(); 301 302 // Draw the players' pieces in the top 303 for(int player = 0; player < 4; player++) 304 { 305 mnNumPiecesPerRow = int(sqrt(mpGame->GetUnplayedPlayerPieces(BlokishID(player)).size()) + 1); 306 glLoadIdentity(); 307 glOrtho(-1, mnNumPiecesPerRow*6 + 1, -1, mnNumPiecesPerRow*6 + 1, -1, 1); 308 309 int boxDimension = size.GetWidth() / 8; 310 int boxBorder = 0; 311 312 glViewport( int((size.GetWidth()*(5.00 + player * 1.25))/ 10) + boxBorder, 313 size.GetHeight() - boxDimension - boxBorder - 1, 314 boxDimension - 2*boxBorder, 315 boxDimension - 2*boxBorder); 316 317 // Draw the box surrounding the pieces 318 if(mpGame->IsDead(BlokishID(player), true)) 319 glColor4f(.35, .35, .35, .75); 320 else 321 glColor4f(mvColor[player][0], mvColor[player][1], mvColor[player][2], 1); 322 323 glPushMatrix(); 324 glLoadIdentity(); 325 glOrtho(0, 1, 0, 1, -1, 1); 326 DrawSquare(BlokishID(player), 1.0f, true, mpGame->IsDead(BlokishID(player), true)); 327 glPopMatrix(); 328 329 DrawUnusedPlayerPieces(BlokishID(player)); 330 } 331 332 mnNumPiecesPerRow = 7; 333 } 334 else 335 { // Draw all the players' unused pieces evenly 336 int maxPieces = 0; 337 maxPieces = std::max(maxPieces, int(mpGame->GetUnplayedPlayerPieces(first).size())); 338 maxPieces = std::max(maxPieces, int(mpGame->GetUnplayedPlayerPieces(second).size())); 339 maxPieces = std::max(maxPieces, int(mpGame->GetUnplayedPlayerPieces(third).size())); 340 maxPieces = std::max(maxPieces, int(mpGame->GetUnplayedPlayerPieces(fourth).size())); 341 342 mnNumPiecesPerRow = int(sqrt(float(maxPieces)) + 1); 343 344 glLoadIdentity(); 345 glOrtho(0, mnNumPiecesPerRow*6, 0, mnNumPiecesPerRow*6, -1, 1); 346 347 glViewport(size.GetWidth() / 2, size.GetHeight()/2, size.GetWidth() / 4, size.GetHeight()/2); 348 glPushMatrix(); 349 glLoadIdentity(); 350 glOrtho(0, 1, 0, 1, -1, 1); 351 DrawSquare(first); 352 glPopMatrix(); 353 DrawUnusedPlayerPieces(first); 354 355 glViewport(size.GetWidth() * 3 / 4, size.GetHeight()/2, size.GetWidth() / 4, size.GetHeight()/2); 356 glPushMatrix(); 357 glLoadIdentity(); 358 glOrtho(0, 1, 0, 1, -1, 1); 359 DrawSquare(second); 360 glPopMatrix(); 361 DrawUnusedPlayerPieces(second); 362 363 glViewport(size.GetWidth() / 2, 0, size.GetWidth() / 4, size.GetHeight()/2); 364 glPushMatrix(); 365 glLoadIdentity(); 366 glOrtho(0, 1, 0, 1, -1, 1); 367 DrawSquare(third); 368 glPopMatrix(); 369 DrawUnusedPlayerPieces(third); 370 371 glViewport(size.GetWidth() * 3 / 4, 0, size.GetWidth() / 4, size.GetHeight()/2); 372 glPushMatrix(); 373 glLoadIdentity(); 374 glOrtho(0, 1, 0, 1, -1, 1); 375 DrawSquare(fourth); 376 glPopMatrix(); 377 DrawUnusedPlayerPieces(fourth); 378 379 mnNumPiecesPerRow = 7; 380 } 381 382 // Draw the selected piece 383 if(mpGame->GetOutcome() < BlokishGame::DOING_AI) 384 { 385 BlokishPiece *selectedPiece = mpGame->GetSelectedPiece(); 386 if(selectedPiece != NULL) 387 { 388 glViewport(0, 0, size.GetWidth(), size.GetHeight()); 389 glLoadIdentity(); 390 glOrtho(0, mpGame->GetNumCols() * 2, 0, mpGame->GetNumRows(), -1, 1); 391 392 float xCoordinate = mpGame->GetNumCols() * 2.0 * mMouseX / float(size.GetWidth()); 393 float yCoordinate = mpGame->GetNumRows() * (size.GetHeight() - mMouseY) / float(size.GetHeight()); 394 395 glTranslatef(xCoordinate - .5, yCoordinate - .5, 0); 396 397 // Translate the window position into a board position 398 int row = int(mpGame->GetNumRows() * (size.GetHeight() - mMouseY) / float(size.GetHeight())); 399 int col = int(mpGame->GetNumCols() * mMouseX / float(size.GetWidth() / 2.0)); 400 if(mpGame->PieceCanBePlaced(selectedPiece, row, col)) 401 DrawPiece(selectedPiece, 1.0f); 402 else 403 DrawPiece(selectedPiece, 0.5f); 404 } 405 } 406 407 SwapBuffers(); 408 } 409 DrawUnusedPlayerPieces(BlokishID player=none)410 void DrawUnusedPlayerPieces(BlokishID player = none) 411 { 412 glPushMatrix(); 413 414 std::vector<BlokishPiece*> &pieces = mpGame->GetUnplayedPlayerPieces(player); 415 416 glTranslatef(3, 3, 0); 417 int piecesDrawn = 0; 418 for(std::vector<BlokishPiece*>::iterator itr = pieces.begin(); itr != pieces.end(); itr++) 419 { 420 DrawPiece(*itr, 1.0f); 421 422 piecesDrawn++; 423 if(piecesDrawn % mnNumPiecesPerRow == 0) 424 { 425 piecesDrawn = 0; 426 glTranslatef(-6 * (mnNumPiecesPerRow - 1), 6, 0); 427 } 428 else 429 { 430 glTranslatef(6, 0, 0); 431 } 432 } 433 glPopMatrix(); 434 } 435 DrawPiece(BlokishPiece * piece,float alpha=1.0f)436 void DrawPiece(BlokishPiece *piece, float alpha = 1.0f) 437 { 438 const std::vector<Square> &squares = piece->GetSquares(); 439 440 for(std::vector<Square>::const_iterator itr = squares.begin(); itr != squares.end(); itr++) 441 { 442 glPushMatrix(); 443 glTranslatef(itr->mX, itr->mY, 0); 444 DrawSquare(piece->GetOwner(), alpha); 445 glPopMatrix(); 446 } 447 } 448 DrawSquare(const BlokishID id,float alpha=1.0,bool drawBox=true,bool gray=false)449 void DrawSquare(const BlokishID id, float alpha = 1.0, bool drawBox = true, bool gray = false) 450 { 451 const float bounds[] = {.15, .85}; 452 453 if(drawBox) 454 { 455 glBegin(GL_LINE_LOOP); 456 glColor4f(0,0,0,.1); 457 glVertex2f(0,0); 458 glVertex2f(0,1); 459 glVertex2f(1,1); 460 glVertex2f(1,0); 461 glEnd(); 462 } 463 464 if(id == usable) 465 { 466 glColor4f(0, 0, 0, .25 * alpha); 467 468 glBegin(GL_TRIANGLE_FAN); 469 glVertex2f(.5, .5); 470 471 for(float i = 0; i <= M_PI * 2; i += .157) 472 { 473 glVertex2f(.5 + .1*cos(i), .5 + .1*sin(i)); 474 } 475 476 glEnd(); 477 } 478 else if(id == first || id == second || id == third || id == fourth) 479 { 480 glBegin(GL_QUADS); 481 if(gray) 482 glColor4f(.75, .75, .75, alpha * .75); 483 else 484 glColor4f(mvColor[id][0] * 1.0, mvColor[id][1] * 1.0, mvColor[id][2] * 1.0, alpha * .75); 485 glVertex2f(bounds[0], bounds[0]); 486 glVertex2f(bounds[0], bounds[1]); 487 glVertex2f(bounds[1], bounds[1]); 488 glVertex2f(bounds[1], bounds[0]); 489 glEnd(); 490 491 glBegin(GL_QUADS); 492 if(gray) 493 glColor4f(.75, .75, .75, alpha); 494 else 495 glColor4f(mvColor[id][0] * 1.0, mvColor[id][1] * 1.0, mvColor[id][2] * 1.0, alpha); 496 glVertex2f( 0, 0); 497 glVertex2f( 0, 1); 498 glVertex2f(bounds[0], bounds[1]); 499 glVertex2f(bounds[0], bounds[0]); 500 501 if(gray) 502 glColor4f(.75, .75, .75, alpha * .80); 503 else 504 glColor4f(mvColor[id][0] * 1.0, mvColor[id][1] * 1.0, mvColor[id][2] * 1.0, alpha * .80); 505 glVertex2f(0, 0); 506 glVertex2f(1, 0); 507 glVertex2f(bounds[1], bounds[0]); 508 glVertex2f(bounds[0], bounds[0]); 509 510 if(gray) 511 glColor4f(.75, .75, .75, alpha * .60); 512 else 513 glColor4f(mvColor[id][0] * 1.0, mvColor[id][1] * 1.0, mvColor[id][2] * 1.0, alpha * .60); 514 glVertex2f(1, 0); 515 glVertex2f(1, 1); 516 glVertex2f(bounds[1], bounds[1]); 517 glVertex2f(bounds[1], bounds[0]); 518 519 if(gray) 520 glColor4f(.75, .75, .75, alpha * .40); 521 else 522 glColor4f(mvColor[id][0] * 1.0, mvColor[id][1] * 1.0, mvColor[id][2] * 1.0, alpha * .40); 523 glVertex2f(0, 1); 524 glVertex2f(1, 1); 525 glVertex2f(bounds[1], bounds[1]); 526 glVertex2f(bounds[0], bounds[1]); 527 glEnd(); 528 529 glBegin(GL_LINE_LOOP); 530 if(gray) 531 glColor4f(.25, .25, .25, 1.0); 532 else 533 glColor4f(mvColor[id][0] * .25, mvColor[id][1] * .25, mvColor[id][2] * .25, 1.0); 534 glVertex2f(0,0); 535 glVertex2f(0,1); 536 glVertex2f(1,1); 537 glVertex2f(1,0); 538 glEnd(); 539 } 540 } 541 OnErase(wxEraseEvent & event)542 void OnErase(wxEraseEvent &event) 543 { 544 event.Skip(); 545 } 546 OnRightDown(wxMouseEvent & event)547 void OnRightDown(wxMouseEvent &event) 548 { 549 ReflectHorizontal(); 550 } 551 ReflectHorizontal()552 void ReflectHorizontal() 553 { 554 BlokishGame::TurnOutcome outcome = mpGame->GetOutcome(); 555 if(outcome == BlokishGame::BeginAIThread || outcome == BlokishGame::WaitForAI || outcome == BlokishGame::FinishAIThread) 556 { // Don't interfere. 557 return; 558 } 559 560 if(mpGame->GetSelectedPiece() != NULL) 561 { 562 mpGame->GetSelectedPiece()->ReflectHorizontal(); 563 } 564 else 565 { 566 PieceVector &pieces = mpGame->GetUnplayedPlayerPieces(); 567 568 for(PieceVector::iterator itr = pieces.begin(); itr != pieces.end(); itr++) 569 { 570 (*itr)->ReflectHorizontal(); 571 } 572 } 573 } 574 OnLeftDown(wxMouseEvent & event)575 void OnLeftDown(wxMouseEvent &event) 576 { 577 SetFocus(); 578 579 BlokishGame::TurnOutcome outcome = mpGame->GetOutcome(); 580 if(outcome > BlokishGame::DOING_AI) 581 { // Don't interfere. 582 return; 583 } 584 585 wxPaintDC(this); 586 SetCurrent(); 587 588 wxSize size = GetClientSize(); 589 590 if(event.GetX() >= size.GetWidth() / 2) 591 { // Picking a new piece 592 float pixel[3]; 593 glReadBuffer(GL_FRONT); 594 glReadPixels(event.GetX(), size.GetHeight() - event.GetY(), 1, 1, GL_RGB, GL_FLOAT, pixel); 595 596 int displayWidth = size.GetWidth() / 2; 597 int displayHeight = size.GetHeight(); 598 599 int displayPieceWidth = displayWidth / mnNumPiecesPerRow; 600 int displayPieceHeight = displayHeight / mnNumPiecesPerRow; 601 602 int x = event.GetX() - size.GetWidth() / 2; 603 int y = size.GetHeight() - event.GetY(); 604 605 int row = y / displayPieceHeight; 606 int col = x / displayPieceWidth; 607 int nPiece = row * mnNumPiecesPerRow + col; 608 609 mpGame->SetSelectedPiece(nPiece); 610 } 611 else 612 { // Clicking on the board 613 if(mpGame->GetSelectedPiece() != NULL) 614 { 615 // Translate the window position into a board position 616 int row = int(mpGame->GetNumRows() * (size.GetHeight() - event.GetY()) / float(size.GetHeight())); 617 int col = int(mpGame->GetNumCols() * event.GetX() / float(size.GetWidth() / 2.0)); 618 619 if(mpGame->PlacePiece(mpGame->GetSelectedPiece(), row, col)) 620 { 621 if(BlokishGame::GameOver == mpGame->FinishTurn()) 622 { 623 Refresh(false); 624 Update(); 625 wxMessageBox(wxString::FromAscii("Game over!")); 626 } 627 } 628 } 629 } 630 } 631 OnMotion(wxMouseEvent & event)632 void OnMotion(wxMouseEvent &event) 633 { 634 mMouseX = event.GetX(); 635 mMouseY = event.GetY(); 636 event.Skip(); 637 Refresh(false); 638 } 639 ChangeColor(int which)640 void ChangeColor(int which) 641 { 642 wxColour newColour = wxGetColourFromUser(this); 643 if(newColour.Ok()) 644 { 645 mvColor[which][0] = newColour.Red() / 255.0f; 646 mvColor[which][1] = newColour.Green() / 255.0f; 647 mvColor[which][2] = newColour.Blue() / 255.0f; 648 } 649 650 WriteSettings(); 651 } 652 OnMousewheel(wxMouseEvent & event)653 void OnMousewheel(wxMouseEvent &event) 654 { 655 if(event.GetWheelRotation() < 0) 656 { 657 RotateClockwise(); 658 } 659 else 660 { 661 RotateCounterClockwise(); 662 } 663 Refresh(false); 664 } 665 OnKeyDown(wxKeyEvent & event)666 void OnKeyDown(wxKeyEvent &event) 667 { 668 if(event.GetKeyCode() == WXK_LEFT) 669 { 670 RotateCounterClockwise(); 671 } 672 else if(event.GetKeyCode() == WXK_RIGHT) 673 { 674 RotateClockwise(); 675 } 676 Refresh(false); 677 } 678 RotateCounterClockwise()679 void RotateCounterClockwise() 680 { 681 BlokishGame::TurnOutcome outcome = mpGame->GetOutcome(); 682 if(outcome == BlokishGame::BeginAIThread || outcome == BlokishGame::WaitForAI || outcome == BlokishGame::FinishAIThread) 683 { // Don't interfere. 684 return; 685 } 686 687 if(mpGame->GetSelectedPiece() != NULL) 688 { 689 mpGame->GetSelectedPiece()->RotateCounterClockwise(); 690 } 691 else 692 { 693 PieceVector &pieces = mpGame->GetUnplayedPlayerPieces(); 694 695 for(PieceVector::iterator itr = pieces.begin(); itr != pieces.end(); itr++) 696 { 697 (*itr)->RotateCounterClockwise(); 698 } 699 } 700 } 701 RotateClockwise()702 void RotateClockwise() 703 { 704 BlokishGame::TurnOutcome outcome = mpGame->GetOutcome(); 705 if(outcome == BlokishGame::BeginAIThread || outcome == BlokishGame::WaitForAI || outcome == BlokishGame::FinishAIThread) 706 { // Don't interfere. 707 return; 708 } 709 710 if(mpGame->GetSelectedPiece() != NULL) 711 { 712 mpGame->GetSelectedPiece()->RotateClockwise(); 713 } 714 else 715 { 716 PieceVector &pieces = mpGame->GetUnplayedPlayerPieces(); 717 718 for(PieceVector::iterator itr = pieces.begin(); itr != pieces.end(); itr++) 719 { 720 (*itr)->RotateClockwise(); 721 } 722 } 723 } 724 OnChangeAI()725 void OnChangeAI() 726 { 727 std::vector<bool> aiStatus; 728 aiStatus.push_back(mpGame->IsComputerControlled(0)); 729 aiStatus.push_back(mpGame->IsComputerControlled(1)); 730 aiStatus.push_back(mpGame->IsComputerControlled(2)); 731 aiStatus.push_back(mpGame->IsComputerControlled(3)); 732 733 AI::AIOptions *optionsVector = mpGame->GetOptions(); 734 735 OptionsDialog options(this, -1, wxString::FromAscii("Options"), aiStatus, optionsVector); 736 737 for(int i = 0; i < 4; i++) 738 { 739 mpGame->SetComputerControlled(i, options.IsAI(i)); 740 optionsVector[i].weightSize = options.GetSizeWeight(i); 741 optionsVector[i].weightFriendlySpaces = options.GetFriendlySpaceWeight(i); 742 optionsVector[i].weightEnemySpaces = options.GetEnemySpaceWeight(i); 743 optionsVector[i].numFriendlyPiecesToCheck = options.GetNumFriendlyPieces(i); 744 optionsVector[i].numEnemyPiecesToCheck = options.GetNumEnemyPieces(i); 745 } 746 747 if(mpGame->GetOutcome() != BlokishGame::GameOver) 748 { 749 if(mpGame->GetOutcome() < BlokishGame::DOING_AI) 750 { 751 if(options.IsAI(int(mpGame->GetCurrentPlayer()))) 752 { 753 mpGame->SetOutcome(BlokishGame::BeginAIThread); 754 } 755 else 756 { 757 mpGame->SetOutcome(BlokishGame::GetPlayerInput); 758 } 759 } 760 } 761 762 WriteSettings(); 763 } 764 765 protected: 766 BlokishGame *mpGame; 767 int mMouseX; 768 int mMouseY; 769 int mnNumPiecesPerRow; 770 std::vector<Color> mvColor; 771 772 DECLARE_EVENT_TABLE() 773 }; 774 775 BEGIN_EVENT_TABLE(BlokishCanvas, wxGLCanvas) 776 EVT_KEY_DOWN(BlokishCanvas::OnKeyDown) 777 EVT_MOTION(BlokishCanvas::OnMotion) 778 EVT_PAINT(BlokishCanvas::OnDraw) 779 EVT_ERASE_BACKGROUND(BlokishCanvas::OnErase) 780 EVT_LEFT_DOWN(BlokishCanvas::OnLeftDown) 781 EVT_RIGHT_DOWN(BlokishCanvas::OnRightDown) 782 EVT_MOUSEWHEEL(BlokishCanvas::OnMousewheel) 783 END_EVENT_TABLE() 784 785 class BlokishFrame:public wxFrame 786 { 787 public: BlokishFrame(wxWindow * parent,wxWindowID id,wxString title)788 BlokishFrame(wxWindow *parent, wxWindowID id, wxString title):wxFrame(parent, id, title, wxDefaultPosition, wxDefaultSize, wxCAPTION | wxMINIMIZE_BOX | wxCLOSE_BOX | wxSYSTEM_MENU), mGauge(NULL), mTimer(this) 789 { 790 wxIcon BlokishIcon(blokish_xpm); 791 SetIcon(BlokishIcon); 792 if(BlokishIcon.Ok() == false) 793 { 794 std::cerr << "Nope.\n"; 795 exit(1); 796 } 797 798 CreateMenuBar(); 799 CreateStatusBar(); 800 801 int attribList[] = {WX_GL_RGBA, WX_GL_DOUBLEBUFFER}; 802 mCanvas = new BlokishCanvas(this, -1, wxDefaultPosition, wxSize(800, 400), attribList, mGame); 803 804 Connect(mTimer.GetId(), wxEVT_TIMER, wxCommandEventHandler(BlokishFrame::OnTimer)); 805 mTimer.Start(100); 806 807 wxBoxSizer *mainSizer = new wxBoxSizer(wxVERTICAL); 808 mainSizer->Add(mCanvas, 0, 0); 809 mainSizer->SetSizeHints(this); 810 SetSizer(mainSizer); 811 CenterOnScreen(); 812 } 813 814 protected: CreateMenuBar()815 void CreateMenuBar() 816 { 817 menubar = new wxMenuBar(); 818 819 wxMenu *file = new wxMenu(wxString::FromAscii("")); 820 file->Append(ID_NEW_GAME, wxString::FromAscii("&New game")); 821 file->Append(ID_SAVE_GAME, wxString::FromAscii("&Save game")); 822 file->Append(ID_LOAD_GAME, wxString::FromAscii("&Load game")); 823 file->AppendSeparator(); 824 file->Append(wxID_EXIT, wxString::FromAscii("E&xit")); 825 826 wxMenu *options = new wxMenu(wxString::FromAscii("")); 827 options->Append(ID_CHANGE_AI, wxString::FromAscii("Artificial intelligence")); 828 options->AppendSeparator(); 829 options->Append(ID_CHANGE_FIRST, wxString::FromAscii("Change player &1's color")); 830 options->Append(ID_CHANGE_SECOND, wxString::FromAscii("Change player &2's color")); 831 options->Append(ID_CHANGE_THIRD, wxString::FromAscii("Change player &3's color")); 832 options->Append(ID_CHANGE_FOURTH, wxString::FromAscii("Change player &4's color")); 833 834 wxMenu *help = new wxMenu(wxString::FromAscii("")); 835 help->Append(ID_SHOW_HELP, wxString::FromAscii("&Help pages")); 836 help->AppendSeparator(); 837 help->Append(ID_SHOW_ABOUT, wxString::FromAscii("&About")); 838 839 menubar->Append(file, wxString::FromAscii("&File")); 840 menubar->Append(options, wxString::FromAscii("&Options")); 841 menubar->Append(help, wxString::FromAscii("&Help")); 842 843 SetMenuBar(menubar); 844 } 845 OnHelp(wxCommandEvent & event)846 void OnHelp(wxCommandEvent &event) 847 { 848 HelpDialog dialog(help_str, this, -1, wxString::FromAscii("Blokish help")); 849 } 850 OnAbout(wxCommandEvent & event)851 void OnAbout(wxCommandEvent &event) 852 { 853 HelpDialog dialog(about_str, this, -1, wxString::FromAscii("About")); 854 } 855 CreateStatusBar()856 void CreateStatusBar() 857 { 858 statusBar = new wxStatusBar(this, -1); 859 statusBar->SetFieldsCount(5); 860 mGauge = new wxGauge(statusBar, -1, mGame.GetNumCols() * mGame.GetNumRows(), wxDefaultPosition); 861 SetStatusBar(statusBar); 862 } 863 OnClose(wxCloseEvent & event)864 void OnClose(wxCloseEvent &event) 865 { 866 if(gAIThread != NULL) 867 { 868 std::cout << "Waiting for thread to complete... " << std::flush; 869 gbAllowNewThreadToStart = false; 870 gAIThread->Delete(); 871 delete gAIThread; 872 gAIThread = NULL; 873 std::cout << "done.\n" << std::flush; 874 } 875 Destroy(); 876 } 877 OnExit(wxCommandEvent & event)878 void OnExit(wxCommandEvent &event) 879 { 880 Close(); 881 } 882 OnNewGame(wxCommandEvent & event)883 void OnNewGame(wxCommandEvent &event) 884 { 885 WaitForAIToFinish(); 886 mGame.Reset(); 887 gbAllowNewThreadToStart = true; 888 } 889 WaitForAIToFinish()890 void WaitForAIToFinish() 891 { 892 if(gAIThread != NULL) 893 { 894 gbAllowNewThreadToStart = false; 895 896 if(gAIThread->IsRunning()) 897 { 898 gAIThread->Wait(); 899 } 900 } 901 } 902 OnSaveGame(wxCommandEvent & event)903 void OnSaveGame(wxCommandEvent &event) 904 { 905 WaitForAIToFinish(); 906 wxString filename = wxFileSelector(wxString::FromAscii("Select a filename to save as"), wxString::FromAscii(""), wxString::FromAscii("default.sav"), wxString::FromAscii(".sav"), wxString::FromAscii("*.sav"), wxSAVE); 907 if(!filename.empty()) 908 { 909 std::ofstream outFile(filename.fn_str(), std::ios::binary); 910 mGame.Write(outFile); 911 } 912 gbAllowNewThreadToStart = true; 913 } 914 OnLoadGame(wxCommandEvent & event)915 void OnLoadGame(wxCommandEvent &event) 916 { 917 WaitForAIToFinish(); 918 wxString filename = wxFileSelector(wxString::FromAscii("Select a game to load"), wxString::FromAscii(""), wxString::FromAscii(""), wxString::FromAscii(".sav"), wxString::FromAscii("*.sav"), wxOPEN | wxFILE_MUST_EXIST); 919 if(!filename.empty()) 920 { 921 std::ifstream inFile(filename.fn_str(), std::ios::binary); 922 if(false == mGame.Read(inFile)) 923 { 924 wxMessageBox(wxString::FromAscii("File given was not a blokish save game.")); 925 } 926 inFile.close(); 927 } 928 gbAllowNewThreadToStart = true; 929 } 930 OnTimer(wxCommandEvent & event)931 void OnTimer(wxCommandEvent &event) 932 { 933 char buffer[256]; 934 935 if(manPrevScores[0] != mGame.GetScore(first)) 936 { 937 sprintf(buffer, "Player 1: %d", mGame.GetScore(first)); 938 statusBar->SetStatusText(wxString::FromAscii(buffer), 0); 939 } 940 941 if(manPrevScores[1] != mGame.GetScore(second)) 942 { 943 sprintf(buffer, "Player 2: %d", mGame.GetScore(second)); 944 statusBar->SetStatusText(wxString::FromAscii(buffer), 1); 945 } 946 947 if(manPrevScores[2] != mGame.GetScore(third)) 948 { 949 sprintf(buffer, "Player 3: %d", mGame.GetScore(third)); 950 statusBar->SetStatusText(wxString::FromAscii(buffer), 3); 951 } 952 953 if(manPrevScores[3] != mGame.GetScore(fourth)) 954 { 955 sprintf(buffer, "Player 4: %d", mGame.GetScore(fourth)); 956 statusBar->SetStatusText(wxString::FromAscii(buffer), 4); 957 } 958 959 if(mGame.GetOutcome() == BlokishGame::WaitForAI) 960 { 961 statusBar->SetStatusText(wxString::FromAscii(""), 2); 962 mGauge->Show(); 963 mGauge->SetValue(mGame.GetNumDone()); 964 } 965 else if(mGame.GetOutcome() == BlokishGame::GameOver) 966 { 967 mGauge->Hide(); 968 statusBar->SetStatusText(wxString::FromAscii("Game is over"), 2); 969 } 970 else if(mGame.GetOutcome() == BlokishGame::BeginAIThread) 971 { 972 mGauge->Hide(); 973 statusBar->SetStatusText(wxString::FromAscii("Starting AI thread"), 2); 974 } 975 else if(mGame.GetOutcome() == BlokishGame::FinishAIThread) 976 { 977 mGauge->Hide(); 978 statusBar->SetStatusText(wxString::FromAscii("Finishing AI thread"), 2); 979 } 980 else 981 { 982 mGauge->Hide(); 983 statusBar->SetStatusText(wxString::FromAscii("Awaiting player input..."), 2); 984 } 985 986 manPrevScores[0] = mGame.GetScore(first); 987 manPrevScores[1] = mGame.GetScore(second); 988 manPrevScores[2] = mGame.GetScore(third); 989 manPrevScores[3] = mGame.GetScore(fourth); 990 991 mTimer.Start(100); 992 mCanvas->OnTimer(); 993 event.Skip(); 994 } 995 OnChangeAI(wxCommandEvent & event)996 void OnChangeAI(wxCommandEvent &event) 997 { 998 mCanvas->OnChangeAI(); 999 } 1000 OnChangeFirst(wxCommandEvent & event)1001 void OnChangeFirst(wxCommandEvent &event) { mCanvas->ChangeColor(0); } OnChangeSecond(wxCommandEvent & event)1002 void OnChangeSecond(wxCommandEvent &event) { mCanvas->ChangeColor(1); } OnChangeThird(wxCommandEvent & event)1003 void OnChangeThird(wxCommandEvent &event) { mCanvas->ChangeColor(2); } OnChangeFourth(wxCommandEvent & event)1004 void OnChangeFourth(wxCommandEvent &event) { mCanvas->ChangeColor(3); } 1005 OnSize(wxSizeEvent & event)1006 void OnSize(wxSizeEvent &event) 1007 { 1008 if(mGauge != NULL) 1009 { 1010 wxRect rect; 1011 statusBar->GetFieldRect(2, rect); 1012 mGauge->SetSize(rect.x + 2, rect.y + 2, rect.width - 4, rect.height - 4); 1013 } 1014 } 1015 1016 int manPrevScores[4]; 1017 BlokishGame::TurnOutcome mLastIdleOutcome; 1018 BlokishCanvas *mCanvas; 1019 BlokishGame mGame; 1020 wxMenuBar *menubar; 1021 wxStatusBar *statusBar; 1022 wxGauge *mGauge; 1023 wxTimer mTimer; 1024 1025 DECLARE_EVENT_TABLE() 1026 }; 1027 1028 BEGIN_EVENT_TABLE(BlokishFrame, wxFrame) 1029 EVT_SIZE(BlokishFrame::OnSize) 1030 EVT_MENU(ID_SHOW_HELP, BlokishFrame::OnHelp) 1031 EVT_MENU(ID_SHOW_ABOUT, BlokishFrame::OnAbout) 1032 EVT_MENU(ID_CHANGE_AI, BlokishFrame::OnChangeAI) 1033 EVT_MENU(ID_NEW_GAME, BlokishFrame::OnNewGame) 1034 EVT_MENU(ID_SAVE_GAME, BlokishFrame::OnSaveGame) 1035 EVT_MENU(ID_LOAD_GAME, BlokishFrame::OnLoadGame) 1036 EVT_MENU(wxID_EXIT, BlokishFrame::OnExit) 1037 EVT_CLOSE(BlokishFrame::OnClose) 1038 1039 EVT_MENU(ID_CHANGE_FIRST, BlokishFrame::OnChangeFirst) 1040 EVT_MENU(ID_CHANGE_SECOND, BlokishFrame::OnChangeSecond) 1041 EVT_MENU(ID_CHANGE_THIRD, BlokishFrame::OnChangeThird) 1042 EVT_MENU(ID_CHANGE_FOURTH, BlokishFrame::OnChangeFourth) 1043 END_EVENT_TABLE() 1044 1045 class BlokishApp:public wxApp 1046 { 1047 public: OnInit()1048 bool OnInit() 1049 { 1050 CreateDocStrs(); 1051 BlokishFrame *frame = new BlokishFrame(NULL, -1, wxString::FromAscii("Blokish")); 1052 frame->Show(); 1053 return true; 1054 } 1055 }; 1056 1057 IMPLEMENT_APP(BlokishApp) 1058