Components and supplies
12 mm pushbutton
Breadboard - 830 contacts
Voltage translator (4 channels)
6mm pushbutton
1.3" OLED display I2C white
Arduino Nano Every with headers
Apps and platforms
Arduino IDE
Project description
Code
Athalie 4.0
cpp
Adapt line #19 to your own OLED display
1/***** ATHALIE *****/ 2 3String version="4.0"; 4 5/* If you want a reduced version of this 6 program with the following characteristics: 7 - user interface limited to the serial 8 monitor of the IDE, no OLED display, 9 no push buttons, 10 - all the functions of the programm 11 available except the timers, 12 - no library needed for compilation, 13 - fast compilation and uploading, 14 you can comment or suppress the next line */ 15#define OLEDDisplay 16 17#ifdef OLEDDisplay 18 #include <U8g2lib.h> 19 U8G2_SH1106_128X64_NONAME_2_HW_I2C 20 u8g2(U8G2_R0); 21 #include <ISR_Timer_Generic.h> 22 ISR_Timer myTimer; 23#endif 24 25#define COMPUTER 0 26#define PLAYER 1 27#define EMPTY 0 28#define BLACK 1 29#define WHITE 2 30#define FAR 4 31#define NOTFAR 0xFB 32#define EDGE 8 33 34const int enterPin=5; 35const int menuPin=4; 36const int downPin=3; 37const int upPin=2; 38const int leftPin=A6; 39const int rightPin=A7; 40int displayWidth, displayHeight; 41 42typedef byte tBoard[10][10]; 43 44tBoard currentBoard; 45byte who, whoPlays=PLAYER; 46bool gameOver=false; 47bool exitGame=false; 48byte level=1; 49bool showCursor=false; 50byte xCursor, yCursor; 51byte xPlay, yPlay; 52byte nbBlack, nbWhite; 53 54bool openingBookActive=true; 55 56byte memoGame[61][3]; 57byte memoGameNum=0; 58 59enum {enter, menu, down, up, left, right}; 60bool buttonPressed[6]= 61 {false, false, false, false, false, false}; 62bool twoPlayersGame=false; 63 64volatile unsigned int blackTime=0, whiteTime=0; 65bool blackTimeActive, 66 whiteTimeActive; 67volatile bool oneSecond=false; 68char message1[14]; 69char message2[14]; 70const int newGameOption=0, twoPlayersOption=1, 71 replayOption=2, resumeOption=3; 72int option; 73 74/* 75 Objects of the class stopWatch are used to 76 perform time dependant actions without 77 blocking the program by delay functions. 78*/ 79class stopWatch 80{ 81 unsigned long previousTime, currentTime; 82 public : 83 unsigned long elapsed; 84 void init(); 85 void now(); 86 stopWatch(); 87}; 88 89void stopWatch::init() 90{ 91 currentTime=millis(); 92 previousTime=currentTime; 93} 94 95void stopWatch::now() 96{ 97 currentTime=millis(); 98 if (currentTime<previousTime) 99 elapsed=0xFFFFFFFFul-previousTime+ 100 currentTime; 101 else 102 elapsed=currentTime-previousTime; 103} 104 105stopWatch::stopWatch() 106{ 107 currentTime=millis(); 108 previousTime=currentTime; 109} 110 111void displayBoard(tBoard board) // On the OLED 112 // display 113{ 114#ifdef OLEDDisplay 115 116 int squareSize=7; 117 int xBoard=displayWidth-8*squareSize-1; 118 int yBoard=displayHeight-8*squareSize-1; 119 for(int i=0; i<9; i++) 120 { 121 u8g2.drawLine(xBoard+squareSize*i, 122 yBoard, 123 xBoard+squareSize*i, 124 yBoard+squareSize*8); 125 u8g2.drawLine(xBoard, 126 yBoard+squareSize*i, 127 xBoard+squareSize*8, 128 yBoard+squareSize*i); 129 } 130 131 u8g2.setFont(u8g2_font_4x6_mf); 132 for(int i=0; i<8; i++) 133 { 134 u8g2.setCursor(xBoard+2+squareSize*i, 135 yBoard-2); 136 u8g2.print(char('A'+i)); 137 u8g2.setCursor(xBoard-5, 138 yBoard+squareSize-1+ 139 squareSize*i); 140 u8g2.print(char('1'+i)); 141 } 142 143 for(byte i=1; i<=8; i++) 144 for(byte j=1; j<=8; j++) 145 { 146 if(board[i][j]==BLACK) 147 u8g2.drawRFrame( 148 xBoard+2+squareSize*(i-1), 149 yBoard+2+squareSize*(j-1), 150 squareSize-3, 151 squareSize-3, 1); 152 153 else if(board[i][j]==WHITE) 154 u8g2.drawRBox( 155 xBoard+2+squareSize*(i-1), 156 yBoard+2+squareSize*(j-1), 157 squareSize-3, 158 squareSize-3, 1); 159 160 else if(showCursor && canPlay(i, j, 161 board, who)) 162 u8g2.drawPixel(xBoard+3+ 163 squareSize*(i-1), 164 yBoard+3+ 165 squareSize*(j-1)); 166 } 167 if(showCursor) 168 { 169 u8g2.drawPixel( 170 xBoard+1+squareSize*(xCursor-1), 171 yBoard+1+squareSize*(yCursor-1)); 172 u8g2.drawPixel( 173 xBoard-1+squareSize*xCursor, 174 yBoard+1+squareSize*(yCursor-1)); 175 u8g2.drawPixel( 176 xBoard+1+squareSize*(xCursor-1), 177 yBoard-1+squareSize*yCursor); 178 u8g2.drawPixel( 179 xBoard-1+squareSize*xCursor, 180 yBoard-1+squareSize*yCursor); 181 182 u8g2.setDrawColor(0); 183 u8g2.drawLine( 184 xBoard+3+squareSize*(xCursor-1), 185 yBoard+squareSize*(yCursor-1), 186 xBoard-3+squareSize*xCursor, 187 yBoard+squareSize*(yCursor-1)); 188 u8g2.drawLine( 189 xBoard+squareSize*(xCursor-1), 190 yBoard+3+squareSize*(yCursor-1), 191 xBoard+squareSize*(xCursor-1), 192 yBoard-3+squareSize*yCursor); 193 u8g2.drawLine( 194 xBoard-3+squareSize*xCursor, 195 yBoard+squareSize*yCursor, 196 xBoard+3+squareSize*(xCursor-1), 197 yBoard+squareSize*yCursor); 198 u8g2.drawLine( 199 xBoard+squareSize*xCursor, 200 yBoard-3+squareSize*yCursor, 201 xBoard+squareSize*xCursor, 202 yBoard+3+squareSize*(yCursor-1)); 203 u8g2.setDrawColor(1); 204 } 205 206#endif 207} 208 209void sendBoard(tBoard board) // To the serial 210 // monitor 211{ 212 String line= 213 " +---+---+---+---+---+---+---+---+"; 214 Serial.println(); 215 Serial.println 216 (" A B C D E F G H"); 217 for(byte i=1; i<=8; i++) 218 { 219 Serial.println(line); 220 Serial.print(" "); 221 Serial.print(i); 222 Serial.print(" |"); 223 for(byte j=1; j<=8; j++) 224 { 225 if(board[j][i]==BLACK) 226 Serial.print(" @ |"); 227 else if(board[j][i]==WHITE) 228 Serial.print(" 0 |"); 229 else if(showCursor && canPlay(j, i, 230 board, who)) 231 Serial.print(" . |"); 232 else Serial.print(" |"); 233 } 234 Serial.println(); 235 } 236 Serial.println(line); 237} 238 239void displayInfos() // On the OLED display 240{ 241#ifdef OLEDDisplay 242 243 int xDisplayInfos=0; 244 char str[16]; 245 246 u8g2.setFont(u8g2_font_5x8_mf); 247 countDisks(currentBoard); 248 u8g2.setCursor(xDisplayInfos, 8); 249 u8g2.print(message1); 250 u8g2.setCursor(xDisplayInfos, 16); 251 u8g2.print(message2); 252 u8g2.drawCircle(xDisplayInfos+17, 26, 3); 253 u8g2.setCursor(xDisplayInfos+28, 30); 254 u8g2.print(nbBlack); 255 u8g2.setCursor(xDisplayInfos+5, 39); 256 sprintf(str, "%02d:%02d:%02d", 257 blackTime/3600, (blackTime%3600)/60, 258 blackTime%60); 259 u8g2.print(str); 260 u8g2.drawDisc(xDisplayInfos+17, 48, 3); 261 u8g2.setCursor(xDisplayInfos+28, 52); 262 u8g2.print(nbWhite); 263 u8g2.setCursor(xDisplayInfos+5, 61); 264 sprintf(str, "%02d:%02d:%02d", 265 whiteTime/3600, (whiteTime%3600)/60, 266 whiteTime%60); 267 u8g2.print(str); 268 if(!gameOver) 269 if(who==BLACK) 270 u8g2.drawFrame(xDisplayInfos, 271 20, 50, 22); 272 else 273 u8g2.drawFrame(xDisplayInfos, 274 42, 50, 22); 275 276#endif 277} 278 279void sendInfos() // To the serial monitor 280{ 281 countDisks(currentBoard); 282 Serial.print("@ : "); 283 Serial.print(nbBlack); 284 Serial.print(" 0 : "); 285 Serial.println(nbWhite); 286 Serial.println(message1); 287 if(!gameOver) 288 if(who==BLACK) 289 Serial.println("@ plays"); 290 else 291 Serial.println("0 plays"); 292 Serial.println(message2); 293} 294 295void displayGame() // On the OLED dipslay 296{ 297#ifdef OLEDDisplay 298 299 u8g2.firstPage(); 300 do 301 { 302 displayBoard(currentBoard); 303 displayInfos(); 304 } 305 while(u8g2.nextPage()); 306 307#endif 308} 309 310void sendGame() // To the serial monitor 311{ 312 sendBoard(currentBoard); 313 sendInfos(); 314} 315 316 317/* The two next functions are the move 318 generator. Their programming is neither 319 elegant nor easy to understand, but it 320 aims for a maximum speed because it is 321 called very often when looking for the 322 best move. 323*/ 324 325bool canMove; 326 327void playOneDirection(byte * square, int dir, 328 byte who) 329{ 330 byte other=who^(BLACK | WHITE); 331 square+=dir; 332 if((*square)!=other) return; 333 do 334 square+=dir; 335 while((*square)==other); 336 if((*square)==who) 337 { 338 canMove=true; 339 while((*square)!=EMPTY) 340 { 341 *square=who; 342 square-=dir; 343 } 344 } 345} 346 347bool play(byte x, byte y, tBoard board, 348 byte who) 349{ 350 byte other=who^(BLACK|WHITE); 351 byte *square; 352 byte k; 353 354 canMove=false; 355 if(board[x][y]==EMPTY) 356 { 357 square=(&board[0][0])+10+y+10*(x-1); 358 359 playOneDirection(square, 1, who); 360 playOneDirection(square, 9, who); 361 playOneDirection(square, 10, who); 362 playOneDirection(square, 11, who); 363 playOneDirection(square,-9, who); 364 playOneDirection(square, -10, who); 365 playOneDirection(square, -11, who); 366 playOneDirection(square, -1, who); 367 368 if(canMove) 369 { 370 *square=who; 371 *(square+1)&=NOTFAR; 372 *(square+9)&=NOTFAR; 373 *(square+10)&=NOTFAR; 374 *(square+11)&=NOTFAR; 375 *(square-9)&=NOTFAR; 376 *(square-10)&=NOTFAR; 377 *(square-11)&=NOTFAR; 378 *(square-1)&=NOTFAR; 379 } 380 } 381 return(canMove); 382} 383 384/* Identify the squares where it is 385 impossible to play because they have 386 no neighbour. 387*/ 388 389void calculateFar(tBoard board) 390{ 391 for(byte i=1; i<=8; i++) 392 for(byte j=1; j<=8; j++) 393 if(board[i][j]==EMPTY || 394 board[i][j]==FAR) 395 { 396 board[i][j]=FAR; 397 for(int k=-1; k<=1; k++) 398 for(int l=-1; l<=1; l++) 399 if(board[i+k][j+l]==BLACK || 400 board[i+k][j+l]==WHITE) 401 { 402 board[i][j]=EMPTY; 403 break; 404 } 405 } 406} 407 408bool canPlay(byte x, byte y, tBoard board, 409 byte who) 410{ 411 byte other=who^(BLACK|WHITE); 412 int k; 413 414 if(board[x][y]!=EMPTY) return(false); 415 else 416 { 417 // East 418 if(board[x+1][y]==other) 419 { 420 for(k=2; k<8-x && 421 board[x+k][y]==other; k++); 422 if(board[x+k][y]==who) 423 return(true); 424 } 425 426 // South-East 427 if(board[x+1][y+1]==other) 428 { 429 for(k=2; k<8-x && k<8-y && 430 board[x+k][y+k]==other; k++); 431 if(board[x+k][y+k]==who) 432 return(true); 433 } 434 435 // South 436 if(board[x][y+1]==other) 437 { 438 for(k=2; k<8-y && 439 board[x][y+k]==other; k++); 440 if(board[x][y+k]==who) 441 return(true); 442 } 443 444 // South-West 445 if(board[x-1][y+1]==other) 446 { 447 for(k=2; k<x && k<8-y && 448 board[x-k][y+k]==other; k++); 449 if(board[x-k][y+k]==who) 450 return(true); 451 } 452 453 // West 454 if(board[x-1][y]==other) 455 { 456 for(k=2; k<x && 457 board[x-k][y]==other; k++); 458 if(board[x-k][y]==who) 459 return(true); 460 } 461 462 // North-West 463 if(board[x-1][y-1]==other) 464 { 465 for(k=2; k<x && k<y && 466 board[x-k][y-k]==other; k++); 467 if(board[x-k][y-k]==who) 468 return(true); 469 } 470 471 // North 472 if(board[x][y-1]==other) 473 { 474 for(k=2; k<y && 475 board[x][y-k]==other; k++); 476 if(board[x][y-k]==who) 477 return(true); 478 } 479 480 // North-East 481 if(board[x+1][y-1]==other) 482 { 483 for(k=2; k<8-x && k<y && 484 board[x+k][y-k]==other; k++); 485 if(board[x+k][y-k]==who) 486 return(true); 487 } 488 return(false); 489 } 490} 491 492int nbMoves(byte who, tBoard board) 493{ 494 byte moves=0; 495 for(int i=1; i<=8; i++) 496 for(int j=1; j<=8; j++) 497 if(canPlay(i, j, board, who)) moves++; 498 return moves; 499} 500 501void countDisks(tBoard board) 502{ 503 nbBlack=0; 504 nbWhite=0; 505 for(byte i=1; i<=8; i++) 506 for(byte j=1; j<=8; j++) 507 { 508 if(board[i][j]==BLACK) 509 nbBlack++; 510 else if (board[i][j]==WHITE) 511 nbWhite++; 512 } 513} 514 515void initBoard() 516{ 517 for(byte i=0; i<10; i++) 518 { 519 currentBoard[0][i]=EDGE; 520 currentBoard[9][i]=EDGE; 521 currentBoard[i][0]=EDGE; 522 currentBoard[i][9]=EDGE; 523 } 524 for(byte i=1; i<=8; i++) 525 for(byte j=1; j<=8; j++) 526 currentBoard[i][j]=EMPTY; 527 currentBoard[4][4]=WHITE; 528 currentBoard[5][5]=WHITE; 529 currentBoard[4][5]=BLACK; 530 currentBoard[5][4]=BLACK; 531 532 calculateFar(currentBoard); 533} 534 535void buttonsManagement(void) 536{ 537 const int debounce=20; 538 const int repeatPeriod=300; 539 const int beginRepeatTime=1000; 540 static stopWatch buttonStopWatch[6]; 541 static stopWatch repeatStopWatch[6]; 542 int buttonState[6]; 543 static int nbButton[6]={0, 0, 0, 0,0 ,0}; 544 545 buttonState[enter]= 546 digitalRead(enterPin); 547 buttonState[menu]= 548 digitalRead(menuPin); 549 buttonState[down]= 550 digitalRead(downPin); 551 buttonState[up]= 552 digitalRead(upPin); 553 buttonState[left]= 554 digitalRead(leftPin); 555 buttonState[right]= 556 digitalRead(rightPin); 557 for(int i=0; i<6; i++) 558 { 559 if(buttonState[i]==HIGH) 560 { 561 buttonStopWatch[i].init(); 562 nbButton[i]=0; 563 buttonPressed[i]=false; 564 } 565 else 566 { 567 buttonStopWatch[i].now(); 568 switch(nbButton[i]) 569 { 570 case 0 : 571 if(buttonStopWatch[i].elapsed> 572 debounce) 573 { 574 buttonPressed[i]=true; 575 nbButton[i]=1; 576 } 577 break; 578 case 1 : 579 if(buttonStopWatch[i].elapsed> 580 beginRepeatTime) 581 { 582 buttonPressed[i]=true; 583 nbButton[i]=2; 584 repeatStopWatch[i].init(); 585 } 586 break; 587 default : 588 repeatStopWatch[i].now(); 589 if(repeatStopWatch[i].elapsed> 590 repeatPeriod) 591 { 592 buttonPressed[i]=true; 593 repeatStopWatch[i].init(); 594 } 595 break; 596 } 597 } 598 } 599} 600 601void playerManagement() 602{ 603 bool playerAction=false; 604 byte xReceived=0, yReceived=0; 605 byte receivedByte; 606 607 while(Serial.available()) Serial.read(); 608 609 while(!playerAction && !exitGame) 610 { 611 buttonsManagement(); 612 if(buttonPressed[down]) 613 { 614 if(yCursor<8) yCursor++; 615 buttonPressed[down]=false; 616 displayGame(); 617 } 618 619 if(buttonPressed[up]) 620 { 621 if(yCursor>1) yCursor--; 622 buttonPressed[up]=false; 623 displayGame(); 624 } 625 626 if(buttonPressed[left]) 627 { 628 if(xCursor>1) xCursor--; 629 buttonPressed[left]=false; 630 displayGame(); 631 } 632 633 if(buttonPressed[right]) 634 { 635 if(xCursor<8) xCursor++; 636 buttonPressed[right]=false; 637 displayGame(); 638 } 639 640 if(buttonPressed[enter]&& 641 canPlay(xCursor, yCursor, 642 currentBoard, who)) 643 { 644 xPlay=xCursor; 645 yPlay=yCursor; 646 buttonPressed[enter]=false; 647 playerAction=true; 648 } 649 650 if(buttonPressed[menu]) 651 { 652 buttonPressed[menu]=false; 653 exitGame=true; 654 } 655 656 if(Serial.available()) 657 { 658 receivedByte=Serial.read(); 659 if(receivedByte>='a' && 660 receivedByte<='h') 661 xReceived=receivedByte-'a'+1; 662 if(receivedByte>='A' && 663 receivedByte<='H') 664 xReceived=receivedByte-'A'+1; 665 if(receivedByte>='1' && 666 receivedByte<='8') 667 yReceived=receivedByte-'1'+1; 668 if(receivedByte==10 || 669 receivedByte==13) 670 if(canPlay(xReceived, yReceived, 671 currentBoard, who)) 672 { 673 xPlay=xReceived; 674 yPlay=yReceived; 675 playerAction=true; 676 } 677 if(receivedByte=='m' || 678 receivedByte=='M') 679 exitGame=true; 680 } 681 682#ifdef OLEDDisplay 683 if(oneSecond) 684 { 685 displayGame(); 686 oneSecond=false; 687 } 688 myTimer.run(); 689#endif 690 691 } 692} 693 694char * opBook[150]; 695int numLines; 696 697void installOpeningBook(void) 698{ 699 numLines=0; 700 opBook[numLines++]="C4C3D3C5D6F4F5D2G4D7C7E7" 701 "C6B5B4E3A6E6F3B3E2"; 702 opBook[numLines++]="C4E3F6E6F5C5C3B4D6C6B3D7" 703 "A4F4B5B6C7"; 704 opBook[numLines++]="C4E3F6E6F5C5C3C6D3D2C2F4" 705 "G3B4"; 706 opBook[numLines++]="C4E3F6E6F5C5F4G5G6D3F3G4" 707 "D6C7H3H4E2"; 708 opBook[numLines++]="C4E3F6E6F5C5C3C6D6G5G4D3" 709 "D2B4F4"; 710 opBook[numLines++]="C4C3D3C5B4D2C2A4E6F3E2F5" 711 "B5A5D6"; 712 opBook[numLines++]="C4E3F4C5D6F3E6C3D3E2D2F2" 713 "F5C6E1F1G1D1C1C2B1D7G4F7" 714 "B4"; 715 opBook[numLines++]="C4C3D3C5B3E3D6C6B5B4E6D7" 716 "F4"; 717 opBook[numLines++]="C4C3D3C5F6E2C6E3B4B5D6B3" 718 "D2B6C2C1D1F5"; 719 opBook[numLines++]="C4E3F5E6F4G5F6E7G3F3G4D3" 720 "H6H4"; 721 opBook[numLines++]="C4E3F4C5E6F5D6D3C3B4F6D2"; 722 opBook[numLines++]="C4E3F6E6F5C5D3C3D6F4F3G3" 723 "F2G4E2D2C2"; 724 opBook[numLines++]="C4C5D6C3E6D7C6C7D3E7B4A3" 725 "B5"; 726 opBook[numLines++]="C4E3F3"; 727 opBook[numLines++]="C4E3F5E6F4C5D6C6F7F3D3E7" 728 "B5G3G4F6G5F8C7H6D7H4"; 729 opBook[numLines++]="C4C3D3C5D6F4F5D2B5B6"; 730 opBook[numLines++]="C4C3D3C5D6F4F5E6C6D7"; 731 opBook[numLines++]="C4C3D3C5D6F4F3E6C6D2"; 732 opBook[numLines++]="C4C3D3C5D6E3F5C6"; 733 opBook[numLines++]="C4C3D3C5D6E3F3F4"; 734 opBook[numLines++]="C4C3D3C5D6E3D2E2"; 735 opBook[numLines++]="C4C3D3C5D6E6C6"; 736 opBook[numLines++]="C4C3D3C5D6E2B3"; 737 opBook[numLines++]="C4C3D3C5D6C7F6"; 738 opBook[numLines++]="C4C3D3C5F6E2C6E6B5D6"; 739 opBook[numLines++]="C4C3D3E3F4D6E6B4E2F2"; 740 opBook[numLines++]="C4C3D3E3F4D6E6F5F3G4"; 741 opBook[numLines++]="C4C3D3E3F4D6C6F5F3B4"; 742 opBook[numLines++]="C4C3D3E3F4C5E6F3"; 743 opBook[numLines++]="C4C3D3E3F4C5C6D6"; 744 opBook[numLines++]="C4C3D3E3F4C5B4B5"; 745 opBook[numLines++]="C4C3D3E3F4F5F3"; 746 opBook[numLines++]="C4C3D3E3F4B5C2"; 747 opBook[numLines++]="C4C3D3E3F4G3F6"; 748 opBook[numLines++]="C4C3D3E3F6B5F3F5E2F4"; 749 opBook[numLines++]="C4C3F5B4D3"; 750 opBook[numLines++]="C4C3E6B4D3"; 751 opBook[numLines++]="C4C3C2C5E6"; 752 opBook[numLines++]="C4E3F4C5D6F3E6B4C3G4"; 753 opBook[numLines++]="C4E3F4C5D6F3E6D7C6"; 754 opBook[numLines++]="C4E3F4C5D6F5D3"; 755 opBook[numLines++]="C4E3F4C5D6E6C6C7"; 756 opBook[numLines++]="C4E3F4C5D6E6D3B4F3C6"; 757 opBook[numLines++]="C4E3F4C5D2C3"; 758 opBook[numLines++]="C4E3F4C5E2C3"; 759 opBook[numLines++]="C4E3F4C5C6B5"; 760 opBook[numLines++]="C4E3F4C5E6C3"; 761 opBook[numLines++]="C4E3F4G5G4C5"; 762 opBook[numLines++]="C4E3F4G3G4C5"; 763 opBook[numLines++]="C4E3F4C3D3C5"; 764 opBook[numLines++]="C4E3F5B4F3F4"; 765 opBook[numLines++]="C4E3F5E6F3C5"; 766 opBook[numLines++]="C4E3F6E6F5C5"; 767 opBook[numLines++]="C4E3F3C5E2C3"; 768 opBook[numLines++]="C4E3F3G3E2C6"; 769 opBook[numLines++]="C4E3F2C6F6F5"; 770 opBook[numLines++]="C4E3F2C3C5C6"; 771 opBook[numLines++]="C4C5D6E3C6B5E6"; 772 opBook[numLines++]="C4C5D6E3C6B4C3"; 773 opBook[numLines++]="C4C5D6E3C6B6B5"; 774 opBook[numLines++]="C4C5D6E3C6C7D7"; 775 opBook[numLines++]="C4C5D6E3D3C3"; 776 opBook[numLines++]="C4C5D6C3E6F6F5"; 777 opBook[numLines++]="C4C5D6C3E6F7E7"; 778 opBook[numLines++]="C4C5D6C3E6E7D7"; 779 opBook[numLines++]="C4C5D6C3E6F5C6"; 780 opBook[numLines++]="C4C5D6C3E6D7C6"; 781 opBook[numLines++]="C4C5D6C3E6F4E3"; 782 opBook[numLines++]="C4C5D6C7D7C3E6"; 783 opBook[numLines++]="C4C5D6C7D7E7C6"; 784 opBook[numLines++]="C4C5D6C7D7E3C6"; 785 opBook[numLines++]="C4C5D6E7D7C3E6"; 786 opBook[numLines++]="C4C5D6E7D7C7C6"; 787 opBook[numLines++]="C4C5D6E7D7E3C6"; 788 opBook[numLines++]="C4C5B6C3"; 789 opBook[numLines++]="C4C5C6B5"; 790 opBook[numLines++]="C4C5E6C3"; 791 opBook[numLines++]="C4C5F6C3"; 792} 793 794/* Play according to the opening book */ 795void opBookPlay(void) 796{ 797 char playFirstQuarter[3]; 798 char memoLine[100]; 799 char nextMove[]="C4"; 800 801 if(memoGameNum==0) 802 { 803 xPlay=3; 804 yPlay=4; 805 } 806 else 807 { 808 strcpy(playFirstQuarter, "C4"); 809 strcpy(memoLine, ""); 810 for(int moveNum=0; moveNum<memoGameNum; 811 moveNum++) 812 { 813 switch(memoGame[0][0]) 814 { 815 case 3 : 816 playFirstQuarter[0]= 817 'A'-1+memoGame[moveNum][0]; 818 playFirstQuarter[1]= 819 '1'-1+memoGame[moveNum][1]; 820 break; 821 822 case 4 : 823 playFirstQuarter[0]= 824 'A'-1+memoGame[moveNum][1]; 825 playFirstQuarter[1]= 826 '1'-1+memoGame[moveNum][0]; 827 break; 828 829 case 5 : 830 playFirstQuarter[0]= 831 'A'+8-memoGame[moveNum][1]; 832 playFirstQuarter[1]= 833 '1'+8-memoGame[moveNum][0]; 834 break; 835 836 case 6 : 837 playFirstQuarter[0]= 838 'A'+8-memoGame[moveNum][0]; 839 playFirstQuarter[1]= 840 '1'+8-memoGame[moveNum][1]; 841 break; 842 } 843 strcat(memoLine, playFirstQuarter); 844 } 845 846 for(int i=0; i<numLines; i++) 847 if(!(strncmp(opBook[i], memoLine, 848 strlen(memoLine))) && 849 strlen(opBook[i])>strlen(memoLine)) 850 { 851 playFirstQuarter[0]= 852 opBook[i][strlen(memoLine)]; 853 playFirstQuarter[1]= 854 opBook[i][strlen(memoLine)+1]; 855 856 switch(memoGame[0][0]) 857 { 858 case 3 : 859 xPlay=playFirstQuarter[0]+1-'A'; 860 yPlay=playFirstQuarter[1]+1-'1'; 861 break; 862 863 case 4 : 864 xPlay=playFirstQuarter[1]+1-'1'; 865 yPlay=playFirstQuarter[0]+1-'A'; 866 break; 867 868 case 5 : 869 xPlay='1'+8-playFirstQuarter[1]; 870 yPlay='A'+8-playFirstQuarter[0]; 871 break; 872 873 case 6 : 874 xPlay='A'+8-playFirstQuarter[0]; 875 yPlay='1'+8-playFirstQuarter[1]; 876 } 877 return; 878 } 879 openingBookActive=false; 880 } 881} 882 883/* The next five functions evaluate a position 884 using a cooking that mixes different 885 criteria such as the mobility, the number 886 of disks of each color, the owning of the 887 corners... The higher the resulting score 888 is, the more the position is favorable to 889 the white player. 890*/ 891int mobility(tBoard board) 892{ 893 int mob=0; 894 byte*square; 895 byte*firstSquare; 896 897 firstSquare=&board[1][1]; 898 for(byte i=0; i<78; i++) 899 { 900 square=firstSquare+i; 901 if(*square==EMPTY) 902 { 903 if(*(square+1)==BLACK) mob++; 904 if(*(square+1)==WHITE) mob--; 905 if(*(square+9)==BLACK) mob++; 906 if(*(square+9)==WHITE) mob--; 907 if(*(square+10)==BLACK) mob++; 908 if(*(square+10)==WHITE) mob--; 909 if(*(square+11)==BLACK) mob++; 910 if(*(square+11)==WHITE) mob--; 911 if(*(square-9)==BLACK) mob++; 912 if(*(square-9)==WHITE) mob--; 913 if(*(square-10)==BLACK) mob++; 914 if(*(square-10)==WHITE) mob--; 915 if(*(square-11)==BLACK) mob++; 916 if(*(square-11)==WHITE) mob--; 917 if(*(square-1)==BLACK) mob++; 918 if(*(square-1)==WHITE) mob--; 919 } 920 } 921 return (mob); 922} 923 924int fiveDiskEdges(tBoard board) 925{ 926 int score=0; 927 928 if((board[1][1]&NOTFAR)==EMPTY) 929 { 930 if((board[1][8]&NOTFAR)==EMPTY) 931 { 932 if(board[1][2]==EMPTY) 933 { 934 if(board[1][3]==board[1][7]) 935 { 936 if(board[1][3]==BLACK) score++; 937 else if(board[1][3]==WHITE) score--; 938 } 939 } 940 else if(board[1][7]==EMPTY) 941 { 942 if(board[1][2]==board[1][6]) 943 { 944 if(board[1][2]==BLACK) score++; 945 else if(board[1][2]==WHITE) score--; 946 } 947 } 948 } 949 if((board[8][1]&NOTFAR)==EMPTY) 950 { 951 if(board[2][1]==EMPTY) 952 { 953 if(board[3][1]==board[7][1]) 954 { 955 if(board[3][1]==BLACK) score++; 956 else if(board[3][1]==WHITE) score--; 957 } 958 } 959 else if(board[7][1]==EMPTY) 960 { 961 if(board[2][1]==board[6][1]) 962 { 963 if(board[2][1]==BLACK) score++; 964 else if(board[2][1]==WHITE) score--; 965 } 966 } 967 } 968 } 969 970 if((board[8][8]&NOTFAR)==EMPTY) 971 { 972 if((board[8][1]&NOTFAR)==EMPTY) 973 { 974 if(board[8][2]==EMPTY) 975 { 976 if(board[8][3]==board[8][7]) 977 { 978 if(board[8][3]==BLACK) score++; 979 else if(board[8][3]==WHITE) score--; 980 } 981 } 982 else if(board[8][7]==EMPTY) 983 { 984 if(board[8][2]==board[8][6]) 985 { 986 if(board[8][2]==BLACK) score++; 987 else if(board[8][2]==WHITE) score--; 988 } 989 } 990 } 991 if((board[1][8]&NOTFAR)==EMPTY) 992 { 993 if(board[2][8]==EMPTY) 994 { 995 if(board[3][8]==board[7][8]) 996 { 997 if(board[3][8]==BLACK) score++; 998 else if(board[3][8]==WHITE) score--; 999 } 1000 } 1001 else if(board[7][8]==EMPTY) 1002 { 1003 if(board[2][8]==board[6][8]) 1004 { 1005 if(board[2][8]==BLACK) score++; 1006 else if(board[2][8]==WHITE) score--; 1007 } 1008 } 1009 } 1010 } 1011 return score; 1012} 1013 1014const int value[4][4]={{200, -30, 0, 0}, 1015 {-30, -50, 0, 0}, 1016 { 0, 0, 0, 0}, 1017 { 0, 0, 0, 0}}; 1018 1019void position(tBoard board, byte * blackDisks, 1020 byte * whiteDisks, 1021 int * diffDisksWithCorner, 1022 int * posScore) 1023{ 1024/* North-west quarter of board */ 1025 if((board[1][1]&NOTFAR)==EMPTY) 1026 { 1027 for(byte i=1; i<5; i++) 1028 for(byte j=1; j<5; j++) 1029 if(board[i][j]==WHITE) 1030 { 1031 (*whiteDisks)++; 1032 (*posScore)+=value[i-1][j-1]; 1033 } 1034 else if(board[i][j]==BLACK) 1035 { 1036 (*blackDisks)++; 1037 (*posScore)-=value[i-1][j-1]; 1038 } 1039 } 1040 else 1041 { 1042 if(board[1][1]==WHITE) 1043 (*posScore)=value[0][0]; 1044 else 1045 (*posScore)=-value[0][0]; 1046 for(byte i=1; i<5; i++) 1047 for(byte j=1; j<5; j++) 1048 if(board[i][j]==WHITE) 1049 { 1050 (*whiteDisks)++; 1051 (*diffDisksWithCorner)++; 1052 } 1053 else if(board[i][j]==BLACK) 1054 { 1055 (*blackDisks)++; 1056 (*diffDisksWithCorner)--; 1057 } 1058 } 1059 1060/* South-west quarter of board */ 1061 if((board[1][8]&NOTFAR)==EMPTY) 1062 { 1063 for(byte i=1; i<5; i++) 1064 for(byte j=8; j>4; j--) 1065 if(board[i][j]==WHITE) 1066 { 1067 (*whiteDisks)++; 1068 (*posScore)+=value[i-1][8-j]; 1069 } 1070 else if(board[i][j]==BLACK) 1071 { 1072 (*blackDisks)++; 1073 (*posScore)-=value[i-1][8-j]; 1074 } 1075 } 1076 else 1077 { 1078 if(board[1][8]==WHITE) 1079 (*posScore)+=value[0][0]; 1080 else 1081 (*posScore)-=value[0][0]; 1082 for(byte i=1; i<5; i++) 1083 for(byte j=8; j>4; j--) 1084 if(board[i][j]==WHITE) 1085 { 1086 (*whiteDisks)++; 1087 (*diffDisksWithCorner)++; 1088 } 1089 else if(board[i][j]==BLACK) 1090 { 1091 (*blackDisks)++; 1092 (*diffDisksWithCorner)--; 1093 } 1094 } 1095 1096/* North-east quarter of board */ 1097 if((board[8][1]&NOTFAR)==EMPTY) 1098 { 1099 for(byte i=8; i>4; i--) 1100 for(byte j=1; j<5; j++) 1101 if(board[i][j]==WHITE) 1102 { 1103 (*whiteDisks)++; 1104 (*posScore)+=value[8-i][j-1]; 1105 } 1106 else if(board[i][j]==BLACK) 1107 { 1108 (*blackDisks)++; 1109 (*posScore)-=value[8-i][j-1]; 1110 } 1111 } 1112 else 1113 { 1114 if(board[8][1]==WHITE) 1115 (*posScore)+=value[0][0]; 1116 else 1117 (*posScore)-=value[0][0]; 1118 for(byte i=8; i>4; i--) 1119 for(byte j=1; j<5; j++) 1120 if(board[i][j]==WHITE) 1121 { 1122 (*whiteDisks)++; 1123 (*diffDisksWithCorner)++; 1124 } 1125 else if(board[i][j]==BLACK) 1126 { 1127 (*blackDisks)++; 1128 (*diffDisksWithCorner)--; 1129 } 1130 } 1131 1132/* South-east quarter of board */ 1133 if((board[8][8]&NOTFAR)==EMPTY) 1134 { 1135 for(byte i=8; i>4; i--) 1136 for(byte j=8; j>4; j--) 1137 if(board[i][j]==WHITE) 1138 { 1139 (*whiteDisks)++; 1140 (*posScore)+=value[8-i][8-j]; 1141 } 1142 else if(board[i][j]==BLACK) 1143 { 1144 (*blackDisks)++; 1145 (*posScore)-=value[8-i][8-j]; 1146 } 1147 } 1148 else 1149 { 1150 if(board[8][8]==WHITE) 1151 (*posScore)+=value[0][0]; 1152 else 1153 (*posScore)-=value[0][0]; 1154 for(byte i=8; i>4; i--) 1155 for(byte j=8; j>4; j--) 1156 if(board[i][j]==WHITE) 1157 { 1158 (*whiteDisks)++; 1159 (*diffDisksWithCorner)++; 1160 } 1161 else if(board[i][j]==BLACK) 1162 { 1163 (*blackDisks)++; 1164 (*diffDisksWithCorner)--; 1165 } 1166 } 1167} 1168 1169int eval(tBoard board) 1170{ 1171 byte blackDisks=0, whiteDisks=0; 1172 int diffDisksWithCorner=0; 1173 int posScore=0, score; 1174 1175#ifdef OLEDDisplay 1176 if(oneSecond) 1177 { 1178 displayGame(); 1179 oneSecond=false; 1180 } 1181 myTimer.run(); 1182#endif 1183 1184 position(board, & blackDisks, & whiteDisks, 1185 & diffDisksWithCorner, & posScore); 1186 1187 if(blackDisks==0) score=30000; 1188 else if(whiteDisks==0) score=-30000; 1189 else if(whiteDisks+blackDisks==64) 1190 if(whiteDisks>blackDisks) 1191 score=30000-blackDisks+whiteDisks; 1192 else if(nbWhite<nbBlack) 1193 score=30000+whiteDisks-blackDisks; 1194 else 1195 score=0; 1196 else score=(mobility(board)*50 + posScore * 1197 (32-(whiteDisks+blackDisks)/4) + 1198 diffDisksWithCorner*20 + 1199 fiveDiskEdges(board)*400); 1200 return score; 1201} 1202 1203/* The next function implements the minimax 1204 and the alpha beta algorithms. It calls 1205 itself recursively to try all possible 1206 moves and their responses until the 1207 chosen level is reached. The final 1208 positions are evaluated and their scores 1209 compared to choose the best move. 1210 The alpha beta algorithm speeds up this 1211 process by pruning the branches which 1212 can't win. To improve the efficiency of 1213 alpha beta algorithm, the moves are 1214 sorted before next level of each 1215 research. 1216*/ 1217 1218 /* Maximum number of moves analyzed from 1219 one position. This value has been 1220 determined experimentally to prevent 1221 an overflow of the 6 kb of RAM: 1222 */ 1223#define maxMoves 10 1224 1225struct tTableMove 1226{ 1227 byte x; 1228 byte y; 1229 int score; 1230}; 1231 1232int bestMove(tBoard board, byte who, 1233 byte level, byte * x, byte * y, 1234 int previousScore, bool stopped) 1235{ 1236 tBoard scratchBoard; 1237 int score, currentScore; 1238 byte xTemp, yTemp; 1239 byte moves=0, k; 1240 1241 struct tTableMove tableMove[maxMoves]; 1242 1243 if(who==BLACK)score=32000; 1244 else score=-32000; 1245 for(byte i=1; i<=8; i++) 1246 for(byte j=1; j<=8; j++) 1247 if(canPlay(i, j, board, who)) 1248 { 1249 memcpy(scratchBoard, board, 100); 1250 play(i, j, scratchBoard, who); 1251 currentScore=eval(scratchBoard); 1252 if(level==1) 1253 { 1254 if((who==BLACK && 1255 currentScore<score) || 1256 (who==WHITE && 1257 currentScore>score)) 1258 { 1259 score=currentScore; 1260 *x=i; 1261 *y=j; 1262 } 1263 moves++; 1264 } 1265 else 1266/* If level >1, sort the moves to improve 1267 the alpha beta efficiency. 1268*/ 1269 { 1270 k=0; 1271 while((k<moves) && 1272 (((who==BLACK) && (currentScore> 1273 tableMove[k].score)) || 1274 ((who==WHITE) && (currentScore< 1275 tableMove[k].score)))) 1276 k++; 1277 for(byte m=moves; m>k; m--) 1278 { 1279 tableMove[m].x=tableMove[m-1].x; 1280 tableMove[m].y=tableMove[m-1].y; 1281 tableMove[m].score= 1282 tableMove[m-1].score; 1283 } 1284 tableMove[k].x=i; 1285 tableMove[k].y=j; 1286 tableMove[k].score=currentScore; 1287 } 1288 if(moves<maxMoves) moves++; 1289 } 1290 if(moves>0) 1291 if(level==1) 1292 return(score); 1293 else 1294 { 1295 for(byte i=0; i<moves; i++) 1296 { 1297 memcpy(scratchBoard, board, 100); 1298 play(tableMove[i].x, tableMove[i].y, 1299 scratchBoard, who); 1300/* Recursive call to bestMove */ 1301 currentScore= 1302 bestMove(scratchBoard, 1303 who^(BLACK | WHITE), 1304 level-1, &xTemp, &yTemp, 1305 score, false); 1306 if((who==BLACK && 1307 currentScore<score) || 1308 (who==WHITE && currentScore>score)) 1309 { 1310 score=currentScore; 1311 * x=tableMove[i].x; 1312 * y=tableMove[i].y; 1313 1314/* Alpha beta algorithm : */ 1315 if((who==BLACK && 1316 score<previousScore) || 1317 (who==WHITE && 1318 score>previousScore)) 1319 { 1320 return score; 1321 } 1322 } 1323 } 1324 } 1325 1326 else /* moves==0 */ 1327 if(!stopped) 1328 { 1329 if(level>1) 1330/* Recursive call to bestMove when a player 1331 can't play 1332 */ 1333 score=bestMove(board, 1334 who^(BLACK | WHITE), level-1, 1335 &xTemp, &yTemp, score, true); 1336 else 1337 score=eval(board); 1338 } 1339 else 1340 { 1341 countDisks(board); 1342 if(nbWhite>nbBlack) 1343 score=30000-nbBlack+nbWhite; 1344 else if(nbWhite<nbBlack) 1345 score=-30000+nbWhite-nbBlack; 1346 else if(nbWhite==nbBlack) 1347 score=0; 1348 } 1349 return score; 1350} 1351 1352void computerManagement() 1353{ 1354 int score=0, previousScore; 1355 byte trueLevel; 1356 bool stopped=false; 1357 1358 if(level==1) 1359 openingBookActive=false; 1360 if(openingBookActive) 1361 opBookPlay(); 1362 if(!openingBookActive) 1363 { 1364 countDisks(currentBoard); 1365 if(level==1) 1366 { 1367 trueLevel=1; 1368 if(nbBlack+nbWhite>58) 1369 trueLevel=20; 1370 } 1371 else 1372 { 1373 if ((level>4) && 1374 (nbBlack+nbWhite)>51 || 1375 (nbBlack+nbWhite)>54) 1376 { 1377 trueLevel=20; 1378 } 1379 else 1380 { 1381 trueLevel=level-1; 1382 if(nbBlack+nbWhite>33) 1383 trueLevel=level; 1384 if(nbBlack+nbWhite>47) 1385 trueLevel=level+1; 1386 } 1387 } 1388 1389 if(who==WHITE) previousScore=32000; 1390 else previousScore=-32000; 1391 score=bestMove(currentBoard, who, 1392 trueLevel, &xPlay, &yPlay, 1393 previousScore, stopped); 1394 } 1395} 1396 1397void initGame() 1398{ 1399 initBoard(); 1400 gameOver=false; 1401 who=BLACK; 1402 blackTimeActive=false; 1403 blackTime=0; 1404 whiteTimeActive=false; 1405 whiteTime=0; 1406 xPlay=0; 1407 yPlay=0; 1408 memoGameNum=0; 1409 memoGame[0][2]=BLACK; 1410 memoGame[1][2]=WHITE; 1411 openingBookActive=true; 1412} 1413 1414void gameManagement() 1415{ 1416 byte receivedByte; 1417 byte other; 1418 char lineLevel[10]; 1419 1420 exitGame=false; 1421 while(!gameOver) 1422 { 1423 other=who^(BLACK|WHITE); 1424 if(nbMoves(who, currentBoard)>0) 1425 { 1426 strcpy(message1, " - "); 1427 if(!twoPlayersGame) 1428 { 1429 sprintf(lineLevel, "level %d", level); 1430 strcat(message1, lineLevel); 1431 } 1432 if(xPlay!=0) 1433 { 1434 sprintf(message1, "%c-%d ", 1435 char(xPlay+'A'-1), yPlay); 1436 if(!twoPlayersGame) 1437 { 1438 sprintf(lineLevel, "level %d", 1439 level); 1440 strcat(message1, lineLevel); 1441 } 1442 } 1443 if(whoPlays==PLAYER) 1444 { 1445 showCursor=true; 1446 strcpy(message2, "Your turn"); 1447 displayGame(); 1448 sendGame(); 1449 Serial.println("M : main menu"); 1450 playerManagement(); 1451 showCursor=false; 1452 if(exitGame) return; 1453 } 1454 else //whoplays==COMPUTER 1455 { 1456 strcpy(message2, "Thinking"); 1457 displayGame(); 1458 sendGame(); 1459 computerManagement(); 1460 } 1461 play(xPlay, yPlay, currentBoard, who); 1462 memoGame[memoGameNum][0]=xPlay; 1463 memoGame[memoGameNum][1]=yPlay; 1464 memoGame[memoGameNum][2]=who; 1465 memoGameNum++; 1466 if(nbMoves(other, currentBoard)>0) 1467 memoGame[memoGameNum][2]=other; 1468 else 1469 memoGame[memoGameNum][2]=who; 1470 if(whoPlays==COMPUTER) 1471 whoPlays=PLAYER; 1472 else //whoPlays==PLAYER 1473 if(!twoPlayersGame) 1474 whoPlays=COMPUTER; 1475 if(who==BLACK) 1476 { 1477 who=WHITE; 1478 blackTimeActive=false; 1479 whiteTimeActive=true; 1480 } 1481 else //who==WHITE 1482 { 1483 who=BLACK; 1484 blackTimeActive=true; 1485 whiteTimeActive=false; 1486 } 1487 } 1488 else //nbMoves==0 1489 { 1490 blackTimeActive=false; 1491 whiteTimeActive=false; 1492 if(nbMoves(other, currentBoard)==0) 1493 { 1494 gameOver=true; 1495 countDisks(currentBoard); 1496 if(nbBlack>nbWhite) 1497 strcpy(message1, "Black wins"); 1498 else if(nbBlack<nbWhite) 1499 strcpy(message1, "White wins"); 1500 else 1501 strcpy(message1, "Tie"); 1502 strcpy(message2, "Hit Enter"); 1503 displayGame(); 1504 sendGame(); 1505 do 1506 { 1507 receivedByte=0; 1508 if(Serial.available()) 1509 receivedByte=Serial.read(); 1510 buttonsManagement(); 1511 } 1512 while(!buttonPressed[enter] && 1513 receivedByte!=10 && 1514 receivedByte!=13); 1515 buttonPressed[enter]=false; 1516 } 1517 else //nbMoves(other)!=0 1518 { 1519 if(whoPlays==PLAYER) 1520 { 1521 sprintf(message1, "%c-%d No move", 1522 char(xPlay+'A'-1), yPlay); 1523 strcpy(message2, "Hit Enter"); 1524 displayGame(); 1525 sendGame(); 1526 do 1527 { 1528 receivedByte=0; 1529 if(Serial.available()) 1530 receivedByte=Serial.read(); 1531 buttonsManagement(); 1532 } 1533 while(!buttonPressed[enter] && 1534 receivedByte!=10 && 1535 receivedByte!=13); 1536 buttonPressed[enter]=false; 1537 if(!twoPlayersGame) 1538 whoPlays=COMPUTER; 1539 } 1540 else //whoPlays==COMPUTER 1541 whoPlays=PLAYER; 1542 if(who==BLACK) 1543 { 1544 who=WHITE; 1545 blackTimeActive=false; 1546 whiteTimeActive=true; 1547 } 1548 else //who==WHITE 1549 { 1550 who=BLACK; 1551 blackTimeActive=true; 1552 whiteTimeActive=false; 1553 } 1554 } 1555 } 1556 } 1557} 1558 1559void replay(void) 1560{ 1561 bool endReplay=false; 1562 bool moveNumChanged=true; 1563 byte moveNum, i; 1564 char receivedChar=0; 1565 1566 while(Serial.available()) Serial.read(); 1567 gameOver=false; 1568 moveNum=memoGameNum; 1569 while(!endReplay) 1570 { 1571 if(moveNumChanged) 1572 { 1573 initBoard(); 1574 for(i=0; i<moveNum; i++) 1575 play(memoGame[i][0], 1576 memoGame[i][1], 1577 currentBoard, memoGame[i][2]); 1578 who=memoGame[moveNum][2]; 1579 if(i>0) 1580 sprintf(message1, "%c-%d", 1581 char(memoGame[i-1][0]+'A'-1), 1582 memoGame[i-1][1]); 1583 else strcpy(message1," -"); 1584 strcpy(message2, "Replay"); 1585 displayGame(); 1586 sendGame(); 1587 Serial.println("B : backward, " 1588 "F : forward, " 1589 "S : start, E : end, " 1590 "R : resume"); 1591 moveNumChanged=false; 1592 } 1593 buttonsManagement(); 1594 if(Serial.available()) 1595 receivedChar=Serial.read(); 1596 if(buttonPressed[up] || 1597 (receivedChar=='s') || 1598 (receivedChar=='S')) 1599 { 1600 buttonPressed[up]=false; 1601 moveNum=0; 1602 moveNumChanged=true; 1603 } 1604 else if(buttonPressed[down] || 1605 (receivedChar=='e') || 1606 (receivedChar=='E')) 1607 { 1608 buttonPressed[down]=false; 1609 moveNum=memoGameNum; 1610 moveNumChanged=true; 1611 } 1612 else if(buttonPressed[left] || 1613 (receivedChar=='b') || 1614 (receivedChar=='B')) 1615 { 1616 buttonPressed[left]=false; 1617 moveNumChanged=true; 1618 if(moveNum>0) moveNum--; 1619 } 1620 else if(buttonPressed[right] || 1621 (receivedChar=='f') || 1622 (receivedChar=='F')) 1623 { 1624 buttonPressed[right]=false; 1625 if(moveNum<memoGameNum) moveNum++; 1626 moveNumChanged=true; 1627 } 1628 else if(buttonPressed[enter] || 1629 (receivedChar=='r') || 1630 (receivedChar=='R')) 1631 { 1632 buttonPressed[enter]=false; 1633 memoGameNum=moveNum; 1634 endReplay=true; 1635 } 1636 } 1637} 1638 1639void displayLogo(void) 1640{ 1641#ifdef OLEDDisplay 1642 u8g2.setFont(u8g2_font_timB08_tf); 1643 u8g2.setCursor(10, 10); 1644 u8g2.print("ATHALIE "); 1645 u8g2.setFont(u8g2_font_5x8_mf); 1646 u8g2.print(" version "); 1647 u8g2.print(version); 1648#endif 1649} 1650 1651void displayBeginnerMenu(void) // On the OLED 1652 // display 1653{ 1654#ifdef OLEDDisplay 1655 u8g2.firstPage(); 1656 do 1657 { 1658 displayLogo(); 1659 u8g2.setFont(u8g2_font_6x12_mf); 1660 u8g2.setCursor(10, 25); 1661 u8g2.print("Who begins ? "); 1662 u8g2.setCursor(20, 40); 1663 if(whoPlays==PLAYER) 1664 u8g2.setDrawColor(0); 1665 u8g2.print("Player"); 1666 u8g2.setDrawColor(1); 1667 u8g2.setCursor(20, 55); 1668 if(whoPlays==COMPUTER) 1669 u8g2.setDrawColor(0); 1670 u8g2.print("Computer"); 1671 u8g2.setDrawColor(1); 1672 } 1673 while(u8g2.nextPage()); 1674#endif 1675} 1676 1677void sendBeginnerMenu(void) // To the serial 1678 // monitor 1679{ 1680 Serial.println(); 1681 Serial.println("Who begins ?"); 1682 Serial.println 1683 ("Type P (player) or C (computer)"); 1684} 1685 1686void displayLevelMenu(void) // On the OLED 1687 // display 1688{ 1689#ifdef OLEDDisplay 1690 u8g2.firstPage(); 1691 do 1692 { 1693 displayLogo(); 1694 u8g2.setFont(u8g2_font_6x12_mf); 1695 u8g2.setCursor(10+ 1696 u8g2.getStrWidth("Game level "), 27); 1697 if(level<7) u8g2.print(level+1); 1698 u8g2.setCursor(10, 40); 1699 u8g2.print("Game level "); 1700 u8g2.setDrawColor(0); 1701 u8g2.print(level); 1702 u8g2.setDrawColor(1); 1703 u8g2.setCursor(10+ 1704 u8g2.getStrWidth("Game level "), 53); 1705 if(level>1) u8g2.print(level-1); 1706 } 1707 while(u8g2.nextPage()); 1708#endif 1709} 1710 1711void sendLevelMenu(void) // To the serial 1712 // monitor 1713{ 1714 Serial.println("Game level ? (1 to 7)"); 1715} 1716 1717void startMenu() 1718{ 1719 byte receivedByte; 1720 bool chooseMade=false; 1721 1722 while(Serial.available()) Serial.read(); 1723 whoPlays=PLAYER; 1724 level=1; 1725 1726 displayBeginnerMenu(); 1727 sendBeginnerMenu(); 1728 while(!chooseMade) 1729 { 1730 buttonsManagement(); 1731 if(buttonPressed[down] || 1732 buttonPressed[up]) 1733 { 1734 whoPlays=whoPlays^(PLAYER|COMPUTER); 1735 buttonPressed[down]=false; 1736 buttonPressed[up]=false; 1737 displayBeginnerMenu(); 1738 } 1739 if(buttonPressed[enter]) 1740 { 1741 buttonPressed[enter]=false; 1742 chooseMade=true; 1743 } 1744 1745 if(Serial.available()) 1746 { 1747 receivedByte=Serial.read(); 1748 if(receivedByte=='p' || 1749 receivedByte=='P') 1750 { 1751 whoPlays=PLAYER; 1752 chooseMade=true; 1753 } 1754 if(receivedByte=='c' || 1755 receivedByte=='C') 1756 { 1757 whoPlays=COMPUTER; 1758 chooseMade=true; 1759 } 1760 } 1761 } 1762 1763 chooseMade=false; 1764 displayLevelMenu(); 1765 sendLevelMenu(); 1766 while(!chooseMade) 1767 { 1768 buttonsManagement(); 1769 if(buttonPressed[down]) 1770 { 1771 if(level>1)level--; 1772 buttonPressed[down]=false; 1773 displayLevelMenu(); 1774 } 1775 if(buttonPressed[up]) 1776 { 1777 if(level<7)level++; 1778 buttonPressed[up]=false; 1779 displayLevelMenu(); 1780 } 1781 if(buttonPressed[enter]) 1782 { 1783 buttonPressed[enter]=false; 1784 chooseMade=true; 1785 } 1786 if(Serial.available()) 1787 { 1788 receivedByte=Serial.read(); 1789 if(receivedByte>='1' && 1790 receivedByte<='7') 1791 { 1792 level=receivedByte-'1'+1; 1793 chooseMade=true; 1794 } 1795 } 1796 } 1797} 1798 1799void displayMainMenu(void) // On the OLED 1800 // display 1801{ 1802#ifdef OLEDDisplay 1803 u8g2.firstPage(); 1804 do 1805 { 1806 displayLogo(); 1807 1808 u8g2.setFont(u8g2_font_6x12_mf); 1809 u8g2.setCursor(15, 23); 1810 if(option==newGameOption) 1811 u8g2.setDrawColor(0); 1812 u8g2.print("New game"); 1813 u8g2.setDrawColor(1); 1814 u8g2.setCursor(15, 36); 1815 if(option==twoPlayersOption) 1816 u8g2.setDrawColor(0); 1817 u8g2.print("Two players game"); 1818 u8g2.setDrawColor(1); 1819 u8g2.setCursor(15, 49); 1820 if(option==replayOption) 1821 u8g2.setDrawColor(0); 1822 u8g2.print("Replay"); 1823 u8g2.setDrawColor(1); 1824 u8g2.setCursor(15, 62); 1825 if(option==resumeOption) 1826 u8g2.setDrawColor(0); 1827 u8g2.print("Resume"); 1828 u8g2.setDrawColor(1); 1829 } 1830 while(u8g2.nextPage()); 1831#endif 1832} 1833 1834void sendMainMenu(void) // To the serial 1835 // monitor 1836{ 1837 Serial.println(); 1838 Serial.println(" *******************"); 1839 Serial.println(" * A T H A L I E *"); 1840 Serial.println(" *******************"); 1841 Serial.print(" version "); 1842 Serial.println(version); 1843 Serial.println(); 1844 Serial.println(" 1 New game"); 1845 Serial.println(" 2 Two players game"); 1846 Serial.println(" 3 Replay"); 1847 Serial.println(" 4 Resume"); 1848} 1849 1850int mainMenu(void) 1851{ 1852 bool optionSelected=false; 1853 byte receivedByte; 1854 1855 option=newGameOption; 1856 displayMainMenu(); 1857 sendMainMenu(); 1858 1859 while(Serial.available()) Serial.read(); 1860 1861 while(!optionSelected) 1862 { 1863 buttonsManagement(); 1864 if(buttonPressed[down]) 1865 { 1866 if(option<resumeOption) option++; 1867 else option=newGameOption; 1868 buttonPressed[down]=false; 1869 displayMainMenu(); 1870 } 1871 if(buttonPressed[up]) 1872 { 1873 if(option>newGameOption) option--; 1874 else option=resumeOption; 1875 buttonPressed[up]=false; 1876 displayMainMenu(); 1877 } 1878 if(buttonPressed[enter]) 1879 { 1880 buttonPressed[enter]=false; 1881 optionSelected=true; 1882 } 1883 1884 if(Serial.available()) 1885 { 1886 receivedByte=Serial.read(); 1887 if(receivedByte>='1' && 1888 receivedByte<='4') 1889 { 1890 option=receivedByte-'1'; 1891 optionSelected=true; 1892 } 1893 } 1894 } 1895} 1896 1897#ifdef OLEDDisplay 1898 1899void timeUpdate(void) 1900{ 1901 if(blackTimeActive) blackTime++; 1902 if(whiteTimeActive) whiteTime++; 1903 oneSecond=true; 1904} 1905 1906#endif 1907 1908void setup() 1909{ 1910 Serial.begin(9600); 1911 pinMode(enterPin, INPUT_PULLUP); 1912 pinMode(menuPin, INPUT_PULLUP); 1913 pinMode(downPin, INPUT_PULLUP); 1914 pinMode(upPin, INPUT_PULLUP); 1915 pinMode(leftPin, INPUT_PULLUP); 1916 pinMode(rightPin, INPUT_PULLUP); 1917 1918#ifdef OLEDDisplay 1919 u8g2.begin(); 1920 displayWidth=u8g2.getDisplayWidth(); 1921 displayHeight=u8g2.getDisplayHeight(); 1922#endif 1923 1924 initBoard(); 1925 who=BLACK; 1926 xCursor=1; 1927 yCursor=1; 1928 1929#ifdef OLEDDisplay 1930 myTimer.init(); 1931 myTimer.setInterval(1000L, timeUpdate); 1932#endif 1933 installOpeningBook(); 1934} 1935 1936void loop() 1937{ 1938 mainMenu(); 1939 switch(option) 1940 { 1941 case newGameOption : 1942 initGame(); 1943 twoPlayersGame=false; 1944 startMenu(); 1945 break; 1946 1947 case twoPlayersOption : 1948 initGame(); 1949 twoPlayersGame=true; 1950 break; 1951 1952 case replayOption : 1953 replay(); 1954 if(!twoPlayersGame) startMenu(); 1955 break; 1956 1957 case resumeOption : 1958 break; 1959 } 1960 gameManagement(); 1961}
Downloadable files
Athalie schematics
Athalie drawing 1.jpg
Comments
Only logged in users can leave comments