#include #include #include #include #include #include #include #include #include #include #include #include // Include charset and sprite resources char charset[2048] = { #embed "../resources/connect4chars.bin" }; char spriteset[2048] = { #embed "../resources/connect4sprites.bin" }; // Address ov video data byte * const Screen = (byte *)0xc800; byte * const Font = (byte *)0xd000; byte * const Color = (byte *)0xd800; byte * const Sprites = (byte *)0xd800; // Values for player 1 pieces and player 2 pieces. // The values 1 and 5 are selected so that the sum of the // values in a row of four will add up to a unique value // in the range of 0..24 for ranking #define PLAYER1_INC 1 #define PLAYER2_INC 5 // Current state of the board struct Board { char fcols[48]; // Pieces in the 6x7 grid using 48 to have a multiplier of 8 char ffree[7]; // Number of free places in each column } board; enum GameState { GS_READY, // Getting ready GS_PLAYER_MOVE, // The players move GS_COMPUTER_MOVE, // The computers move GS_PLAYER_WIN, // The player wins GS_COMPUTER_WIN, // The computer wins GS_GAME_OVER // No more moves }; // Draw the board into the CharWin using the small // character set void board_draw(CharWin * cwin) { for(char y=0; y<6; y++) { for(char x=0; x<7; x++) { if (board.fcols[8 * y + x] == PLAYER1_INC) cwin_putat_char_raw(cwin, x, y, 82, 10); else if (board.fcols[8 * y + x] == PLAYER2_INC) cwin_putat_char_raw(cwin, x, y, 82, 15); else cwin_putat_char_raw(cwin, x, y, 82, 8); } // Right most column cwin_putat_char_raw(cwin, 7, y, 83, 8); } } // Draw a big piece or place with the given color, the // color 0 denotes an empty place. Empty places use different // tiles to allow sprite priority to obscure the falling pieces void board_draw_item(CharWin * cwin, char x, char y, char c) { char cx = x * 3 + 1, cy = y * 3 + 2; if (c) { cwin_putat_char_raw(cwin, cx + 0, cy + 0, 73, c); cwin_putat_char_raw(cwin, cx + 1, cy + 0, 74, c); cwin_putat_char_raw(cwin, cx + 2, cy + 0, 75, c); cwin_putat_char_raw(cwin, cx + 0, cy + 1, 76, c); cwin_putat_char_raw(cwin, cx + 1, cy + 1, 77, c); cwin_putat_char_raw(cwin, cx + 2, cy + 1, 78, c); cwin_putat_char_raw(cwin, cx + 0, cy + 2, 79, c); cwin_putat_char_raw(cwin, cx + 1, cy + 2, 80, c); cwin_putat_char_raw(cwin, cx + 2, cy + 2, 81, c); } else { cwin_putat_char_raw(cwin, cx + 0, cy + 0, 64, 14); cwin_putat_char_raw(cwin, cx + 1, cy + 0, 65, 14); cwin_putat_char_raw(cwin, cx + 2, cy + 0, 66, 14); cwin_putat_char_raw(cwin, cx + 0, cy + 1, 67, 14); cwin_putat_char_raw(cwin, cx + 1, cy + 1, 68, 14); cwin_putat_char_raw(cwin, cx + 2, cy + 1, 69, 14); cwin_putat_char_raw(cwin, cx + 0, cy + 2, 70, 14); cwin_putat_char_raw(cwin, cx + 1, cy + 2, 71, 14); cwin_putat_char_raw(cwin, cx + 2, cy + 2, 72, 14); } } // Draw the big board void board_draw_main(CharWin * cwin) { for(char y=0; y<6; y++) { for(char x=0; x<7; x++) { if (board.fcols[8 * y + x] == PLAYER1_INC) board_draw_item(cwin, x, y, 10); else if (board.fcols[8 * y + x] == PLAYER2_INC) board_draw_item(cwin, x, y, 15); else board_draw_item(cwin, x, y, 0); } } } // Draw the decoration of the big board void board_init_main(CharWin * cwin) { // Frame around the board cwin_putat_char_raw(cwin, 0, 1, 84, 14); cwin_fill_rect_raw(cwin, 1, 1, 21, 1, 85, 14); cwin_putat_char_raw(cwin, 22, 1, 86, 14); cwin_fill_rect_raw(cwin, 0, 2, 1, 18, 87, 14); cwin_fill_rect_raw(cwin, 22, 2, 1, 18, 88, 14); cwin_putat_char_raw(cwin, 0, 20, 89, 14); cwin_fill_rect_raw(cwin, 1, 20, 21, 1, 90, 14); cwin_putat_char_raw(cwin, 22, 20, 91, 14); // Fields in the board for(char x=0; x<7; x++) cwin_putat_char_raw(cwin, 3 * x + 2, 0, '1' + x, 1); board_draw_main(cwin); } // Play animation of a dropped piece void item_drop_anim(CharWin * cwin, char x, char y, char c) { // Initial position int ix = (cwin->sx + 3 * x + 1) * 8 + 24, iy = (cwin->sy + 2) * 8 + 20; // Show sprite at start position spr_set(0, true, ix, iy, 97, c, true, false, false); // Set sprite priority to be behind background vic.spr_priority = 0x01; // Speed and target position, using four fractional bits int vy = 0, aiy = iy * 16, ty = ((cwin->sy + y * 3 + 2) * 8 + 50) * 16; // Bounce three times back when reaching bottom char bounce = 3; // Loop through the bounces while (bounce > 0) { // Let gravity do its thing of accelerating vy += 3; // Add vertical velocity to position aiy += vy; // Reached the bottom yet? if (aiy > ty) { // Reflect position aiy = 2 * ty - aiy; // Reflect speed vy = - vy * 5 >> 4; // One bounce down bounce--; } // Move sprite and wait for display spr_move(0, ix, aiy >> 4); vic_waitFrame(); } } // Play column selection animation void column_select_anim(CharWin * cwin, char x0, char x1, char c) { // Start position int ix = (cwin->sx + 3 * x0 + 1) * 8 + 24, iy = (cwin->sy + 2) * 8 + 20; // Target position int tx = (cwin->sx + 3 * x1 + 1) * 8 + 24; // Show sprite at start position spr_set(0, true, ix, iy, 97, c, true, false, false); // Set sprite priority to be behind background vic.spr_priority = 0x01; // Not at target position already? if (ix != tx) { // Eight frames for movement for(int i=1; i<=8; i++) { vic_waitFrame(); // Move sprite along path spr_move(0, (ix * (8 - i) + tx * i) >> 3, iy); } } } // Initialize the board to empty void board_init(void) { // Column has six free slots for(char x=0; x<7; x++) board.ffree[x] = 6; // Clear all slots for(char i=0; i<48; i++) board.fcols[i] = 0; } // Drop one piece of player p down column x inline bool board_drop(char x, bool p) { // Check for free space if (board.ffree[x]) { // Fill one slot char y = --(board.ffree[x]); board.fcols[8 * y + x] = p ? PLAYER2_INC : PLAYER1_INC; // Success return true; } return false; } // Remove the top piece from column x inline void board_undrop(char x) { // Free one slot char y = (board.ffree[x])++; board.fcols[8 * y + x] = 0x00; } // Score for rows with 0, 1, 2, and 3 elements of same color static const int score[5] = {0, 1, 8, 128, 10000}; // Score index is calculated (25 * rv + 5 * p1 + p2) with rv the // value range of the row, p1 and p2 number of pieces from player 1 or 2 int fscore[125]; // Indices of the board positions of four slots of each of the 69 // 4 slot rows in the board. The fifth value is the value range // of the row char frows[5][69]; #pragma align(fscore, 256) // Tables for opening library for computer move 1, 2, 3 and 4 const char open1 = 3; const char open2[4 * 7] = { 4, 3, 3, 3, 4, 3, 3, 3, 5, 3, 3, 4, 3, 3, 3, 3, 2, 3, 2, 2, 2, 4, 2, 4, 3, 2, 4, 2 }; const char open3[4 * 7 * 7] = { 0, 5, 2, 2, 5, 3, 2, 5, 4, 2, 4, 3, 3, 5, 2, 2, 3, 2, 3, 2, 2, 4, 5, 2, 4, 4, 3, 2, 3, 3, 3, 3, 3, 3, 4, 1, 3, 2, 5, 3, 2, 1, 2, 5, 2, 2, 4, 1, 4, 5, 4, 2, 4, 3, 3, 5, 4, 1, 3, 4, 4, 4, 3, 2, 1, 3, 2, 3, 2, 3, 5, 5, 1, 5, 4, 3, 3, 3, 4, 3, 3, 5, 3, 3, 3, 5, 2, 3, 4, 1, 3, 5, 4, 3, 1, 4, 3, 5, 2, 2, 3, 2, 3, 2, 2, 2, 1, 3, 2, 3, 2, 3, 3, 1, 3, 2, 2, 3, 3, 2, 1, 3, 2, 2, 2, 2, 3, 3, 3, 2, 1, 3, 3, 3, 3, 1, 3, 3, 2, 3, 2, 3, 3, 3, 3, 3, 3, 3, 2, 1, 2, 3, 4, 3, 2, 4, 3, 2, 2, 2, 2, 1, 0, 2, 4, 3, 4, 4, 4, 4, 1, 2, 5, 2, 2, 2, 2, 3, 2, 4, 6, 5, 4, 4, 4, 4, 3, 2, 4, 3, 2, 3, 4, 5, 4, 3, }; const char open4[4 * 7 * 7 * 7] = { 2, 5, 2, 2, 5, 3, 2, 2, 2, 6, 2, 2, 2, 2, 0, 4, 4, 3, 2, 3, 3, 5, 5, 5, 5, 5, 1, 1, 2, 2, 6, 2, 2, 2, 2, 0, 4, 4, 4, 3, 5, 4, 1, 5, 1, 1, 1, 1, 1, 0, 4, 2, 4, 3, 4, 5, 5, 1, 4, 5, 5, 3, 2, 3, 3, 1, 1, 1, 1, 3, 5, 5, 4, 5, 5, 4, 4, 3, 3, 3, 1, 3, 3, 3, 3, 3, 3, 1, 3, 3, 3, 5, 4, 2, 5, 3, 1, 4, 0, 3, 6, 2, 3, 3, 3, 3, 3, 1, 1, 1, 1, 3, 3, 3, 3, 2, 3, 3, 3, 2, 1, 6, 2, 4, 5, 2, 3, 3, 3, 2, 3, 3, 3, 3, 1, 5, 5, 3, 3, 3, 3, 3, 0, 2, 3, 3, 3, 0, 5, 2, 2, 5, 1, 2, 4, 4, 2, 4, 5, 4, 2, 2, 2, 3, 2, 2, 2, 2, 2, 5, 5, 2, 2, 1, 2, 4, 3, 4, 4, 3, 3, 4, 1, 3, 2, 1, 4, 4, 1, 4, 5, 4, 4, 4, 2, 4, 0, 3, 3, 4, 3, 3, 3, 3, 3, 3, 1, 3, 5, 3, 3, 3, 3, 2, 2, 4, 3, 4, 3, 4, 4, 3, 3, 4, 3, 3, 2, 3, 3, 5, 6, 3, 5, 4, 5, 5, 3, 3, 3, 3, 2, 3, 3, 3, 5, 0, 5, 3, 4, 4, 3, 2, 3, 3, 3, 1, 3, 3, 3, 3, 1, 5, 5, 3, 3, 3, 3, 3, 2, 1, 4, 3, 1, 3, 3, 3, 4, 3, 3, 3, 2, 2, 3, 2, 5, 5, 4, 2, 5, 3, 1, 4, 2, 1, 0, 2, 4, 4, 3, 2, 4, 5, 4, 2, 5, 3, 1, 4, 3, 3, 0, 2, 3, 3, 3, 4, 2, 4, 4, 2, 2, 4, 3, 3, 3, 4, 6, 3, 3, 2, 5, 3, 1, 4, 2, 1, 2, 2, 3, 2, 2, 4, 6, 0, 4, 2, 4, 3, 4, 5, 5, 1, 4, 5, 5, 3, 2, 3, 3, 1, 1, 1, 1, 3, 5, 5, 4, 5, 5, 4, 4, 3, 3, 3, 1, 3, 3, 3, 3, 3, 3, 1, 3, 3, 3, 5, 4, 2, 5, 3, 1, 4, 2, 2, 6, 2, 2, 2, 2, 4, 4, 3, 4, 4, 4, 3, 4, 1, 3, 5, 3, 3, 3, 2, 2, 6, 2, 2, 2, 2, 5, 1, 3, 3, 5, 4, 3, 2, 2, 6, 2, 2, 2, 2, 4, 1, 3, 5, 3, 1, 3, 3, 3, 1, 1, 1, 1, 3, 6, 2, 3, 1, 4, 5, 4, 3, 3, 3, 2, 3, 3, 3, 1, 2, 3, 1, 4, 1, 1, 3, 3, 3, 4, 3, 2, 3, 1, 3, 1, 1, 4, 2, 1, 3, 3, 3, 2, 3, 3, 3, 4, 4, 2, 4, 5, 4, 2, 4, 1, 6, 4, 4, 4, 2, 1, 3, 2, 4, 3, 1, 1, 4, 4, 4, 4, 4, 4, 2, 3, 3, 4, 4, 3, 3, 4, 3, 5, 3, 1, 3, 1, 3, 5, 2, 2, 5, 4, 3, 5, 3, 3, 3, 1, 3, 5, 3, 3, 1, 4, 4, 5, 3, 3, 3, 1, 3, 2, 2, 2, 3, 3, 3, 4, 4, 3, 3, 4, 3, 5, 3, 3, 5, 5, 3, 5, 4, 2, 5, 5, 3, 5, 3, 3, 3, 1, 1, 5, 3, 3, 3, 3, 1, 3, 3, 3, 5, 1, 1, 3, 4, 3, 3, 1, 3, 1, 1, 4, 2, 1, 5, 5, 2, 1, 4, 1, 1, 5, 4, 2, 5, 5, 3, 5, 3, 3, 2, 3, 5, 5, 1, 3, 3, 3, 5, 3, 3, 3, 5, 4, 2, 5, 3, 1, 4, 2, 1, 1, 4, 3, 4, 4, 3, 3, 3, 2, 3, 3, 3, 5, 3, 2, 5, 4, 3, 3, 3, 3, 3, 1, 1, 5, 3, 3, 3, 3, 5, 3, 3, 3, 4, 3, 2, 2, 3, 1, 6, 0, 3, 6, 2, 3, 3, 3, 3, 3, 1, 1, 1, 1, 3, 3, 3, 3, 2, 3, 3, 3, 2, 1, 6, 2, 4, 5, 2, 3, 3, 3, 2, 3, 3, 3, 3, 1, 5, 5, 3, 3, 3, 3, 3, 0, 2, 3, 3, 3, 3, 3, 1, 1, 1, 1, 3, 6, 2, 3, 1, 4, 5, 4, 3, 3, 3, 2, 3, 3, 3, 1, 2, 3, 1, 4, 1, 1, 3, 3, 3, 4, 3, 2, 3, 1, 3, 1, 1, 4, 2, 1, 3, 3, 3, 2, 3, 3, 3, 3, 3, 3, 4, 3, 3, 3, 2, 3, 1, 3, 0, 1, 1, 3, 3, 3, 3, 4, 3, 3, 3, 3, 3, 3, 2, 2, 3, 4, 4, 4, 2, 2, 3, 4, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 2, 2, 3, 2, 2, 2, 2, 1, 3, 2, 4, 3, 1, 1, 0, 1, 2, 2, 4, 5, 3, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 2, 2, 3, 3, 2, 3, 3, 2, 2, 3, 2, 3, 1, 3, 4, 3, 3, 3, 1, 1, 4, 4, 2, 4, 2, 3, 4, 3, 4, 3, 4, 4, 3, 2, 3, 2, 3, 3, 3, 1, 2, 2, 2, 4, 4, 2, 3, 4, 4, 4, 3, 5, 3, 3, 2, 4, 4, 3, 3, 3, 3, 1, 5, 5, 3, 3, 3, 1, 3, 1, 1, 4, 2, 1, 3, 1, 1, 3, 3, 1, 3, 2, 3, 3, 2, 2, 3, 3, 3, 4, 4, 4, 3, 5, 3, 3, 3, 1, 2, 2, 5, 3, 3, 1, 3, 5, 3, 3, 3, 1, 3, 3, 3, 4, 3, 3, 3, 3, 1, 1, 2, 1, 3, 0, 1, 3, 3, 4, 3, 3, 2, 3, 3, 2, 2, 3, 2, 3, 2, 4, 4, 3, 3, 3, 3, 1, 3, 5, 3, 3, 3, 3, 3, 3, 2, 3, 3, 6, 0, 2, 1, 2, 3, 4, 4, 4, 1, 3, 3, 4, 4, 2, 4, 3, 2, 4, 3, 3, 4, 3, 3, 2, 4, 4, 4, 3, 3, 2, 1, 4, 3, 3, 3, 4, 2, 1, 4, 5, 4, 2, 4, 2, 1, 2, 3, 4, 4, 2, 2, 1, 2, 1, 2, 2, 1, 1, 3, 3, 3, 5, 1, 2, 5, 3, 2, 5, 3, 3, 2, 3, 1, 1, 1, 6, 2, 1, 4, 4, 1, 4, 6, 5, 2, 2, 1, 6, 6, 2, 4, 2, 2, 1, 2, 5, 4, 2, 4, 3, 2, 4, 3, 3, 4, 4, 3, 3, 3, 3, 1, 1, 1, 0, 3, 3, 3, 3, 3, 1, 0, 2, 2, 4, 5, 2, 1, 0, 3, 1, 3, 1, 3, 1, 0, 2, 5, 2, 2, 5, 1, 0, 3, 2, 2, 5, 2, 2, 2, 1, 4, 4, 4, 2, 2, 4, 0, 2, 4, 2, 4, 5, 4, 2, 1, 5, 4, 1, 2, 1, 2, 2, 2, 4, 2, 5, 2, 1, 5, 4, 2, 1, 2, 4, 2, 4, 6, 2, 4, 4, 2, 2, 2, 5, 4, 4, 4, 1, 4, 2, 3, 6, 5, 1, 4, 4, 1, 4, 6, 5, 3, 5, 3, 5, 3, 6, 5, 2, 1, 2, 4, 4, 6, 5, 3, 3, 3, 3, 3, 6, 5, 5, 5, 3, 3, 3, 3, 2, 2, 3, 3, 2, 4, 3, 2, 4, 2, 1, 4, 5, 4, 2, 2, 4, 0, 0, 5, 4, 2, 1, 0, 2, 5, 2, 2, 5, 4, 0, 5, 5, 5, 3, 4, 3, 3, 1, 4, 3, 1, 2, 5, 1, 3, 3, 3, 5, 5, 2, 2, 5, 4, 5, 4, 4, 2, 2, 3, 4, 5, 4, 2, 2, 2, 1, 2, 5, 4, 2, 3, 3, 3, 2, 5, 4, 3, 3, 2, 2, 2, 4, 3, 3, 2, 3, 3, 2, 4, 3, 2, 4, 2, 2, 3, 3, 5, 2, 2, 2, 3, 4, 5, 4, 6, }; // Init the ai tables void ai_init(void) { // Loop over 5 value ranges, and 0 to 4 pieces of each player // in a row for(char k=0; k<5; k++) { for(char i=0; i<5; i++) { for(char j=0; j<5; j++) { if (i && j) fscore[25 * k + PLAYER2_INC * i + j] = 0; else if (i == 4 || j == 4) fscore[25 * k + PLAYER2_INC * i + j] = score[i] - score[j]; else fscore[25 * k + PLAYER2_INC * i + j] = (score[i] - score[j]) * (1 + k); } } } // Create the indices of all 69 rows char p = 0; // Vertical rows for(char x=0; x<7; x++) { for(char y=0; y<3; y++) { frows[0][p] = x + 8 * y; frows[1][p] = x + 8 * y + 8; frows[2][p] = x + 8 * y + 16; frows[3][p] = x + 8 * y + 24; frows[4][p] = 25 * (y + 1); p++; } } for(char x=0; x<4; x++) { // Horizontal rows for(char y=0; y<6; y++) { frows[0][p] = x + 8 * y; frows[1][p] = x + 8 * y + 1; frows[2][p] = x + 8 * y + 2; frows[3][p] = x + 8 * y + 3; frows[4][p] = y > 0 ? 25 * (y - 1) : 0; p++; } // Diagonal down rows for(char y=0; y<3; y++) { frows[0][p] = x + 8 * y; frows[1][p] = x + 8 * y + 9; frows[2][p] = x + 8 * y + 18; frows[3][p] = x + 8 * y + 27; frows[4][p] = 25 * (y + 1); p++; } // Diagonal up rows for(char y=3; y<6; y++) { frows[0][p] = x + 8 * y; frows[1][p] = x + 8 * y - 7; frows[2][p] = x + 8 * y - 14; frows[3][p] = x + 8 * y - 21; frows[4][p] = 25 * (y - 2); p++; } } } // Evaluate the current board state for the player int board_eval(bool player) { int score = 0; // Loop through all rows for(char r=0; r<69; r++) { // Build score index for row char sum = board.fcols[frows[0][r]] + board.fcols[frows[1][r]] + board.fcols[frows[2][r]] + board.fcols[frows[3][r]] + frows[4][r]; // Help compiler to avoid two byte pointer arithmetic __assume(sum < 128); // Add score to board score score += fscore[sum]; } // Check if there was a row with four winning pieces and // normalize to +/- 32000 then if (score < -8192) score = -32000; else if (score >= 8192) score = 32000; // Invert score for other player return player ? score : -score; } CharWin cwt, cws; CharWin cw, cwm; char buff[20]; static const char optx[7] = {3, 2, 4, 1, 5, 0, 6}; // Find a good move for the player using alpha/beta pruning // see: https://en.wikipedia.org/wiki/Alpha-beta_pruning int board_check(char level, int alpha, int beta, bool player) { int best = alpha; bool checked = false; // Current score to cutof early if clear win condition int val = board_eval(player) + level; // Best move char zx = 0; // Check for end of search if (level < 5 && val < 5000 && val > -5000) { // Update current check display at level 3 if (level == 3) { board_draw(&cwt); itoa(val, buff, 10); cwin_fill_rect(&cwt, 0, 6, 7, 1, ' ', 1); cwin_putat_string(&cwt, 0, 6, buff, 1); } // Check all seven columns for(char i=0; i<7; i++) { // Start check in the center, due to higher value of // pieces there char ix = optx[i]; // Try to drop a piece if (board_drop(ix, player)) { // We still have a move checked = true; // Check score of position int score = - board_check(level + 1, -beta, -best, !player); // Better than what we have so far if (score > best) { // Remember best = score; zx = ix; // Update best move if on level zero if (level == 0) { board_draw(&cw); itoa(best, buff, 10); cwin_fill_rect(&cw, 0, 6, 7, 1, ' ', 1); cwin_putat_string(&cw, 0, 6, buff, 1); } // Check for beta pruning if (best > beta) { board_undrop(ix); break; } } // Undo move board_undrop(ix); } } } // Return new position if first move level if (level == 0) return zx; else if (checked) return best; else return val; } // Return a move from the opening library if available char board_opening(char step, char * moves) { switch (step) { case 1: return open1; case 2: if (moves[0] < 4) return open2[moves[0] * 7 + moves[1]]; else return 6 - open2[(6 - moves[0]) * 7 + (6 - moves[1])]; case 3: if (moves[0] < 4) return open3[moves[0] * 49 + moves[1] * 7 + moves[2]]; else return 6 - open3[(6 - moves[0]) * 49 + (6 - moves[1]) * 7 + (6 - moves[2])]; case 4: if (moves[0] < 4) return open4[moves[0] * 343 + moves[1] * 49 + moves[2] * 7 + moves[3]]; else return 6 - open4[(6 - moves[0]) * 343 + (6 - moves[1]) * 49 + (6 - moves[2]) * 7 + (6 - moves[3])]; default: return 0xff; } } // Current state of the game struct Game { GameState state; // State char count; // Auto continue counter char posx; // Column osition of new piece char step; // Number of player moves char moves[21]; // Pieces placed by player } TheGame; // Set new game state void game_state(GameState state) { // Set new state TheGame.state = state; // Clear status line cwin_fill_rect(&cws, 0, 0, 40, 1, ' ', 1); switch (state) { case GS_READY: // Start play in one second TheGame.count = 60; TheGame.step = 0; // Clear the board board_init(); board_init_main(&cwm); break; case GS_PLAYER_MOVE: // Place selection sprite in center TheGame.posx = 3; column_select_anim(&cwm, 3, 3, 7); cwin_putat_string(&cws, 0, 0, P"PLAYER MOVE", 7); break; case GS_COMPUTER_MOVE: cwin_putat_string(&cws, 0, 0, P"COMPUTER MOVE", 2); break; case GS_PLAYER_WIN: cwin_putat_string(&cws, 0, 0, P"PLAYER WINS", 7); // Continue in 4 seconds TheGame.count = 240; break; case GS_COMPUTER_WIN: cwin_putat_string(&cws, 0, 0, P"COMPUTER WINS", 2); // Continue in 4 seconds TheGame.count = 240; break; case GS_GAME_OVER: cwin_putat_string(&cws, 0, 0, P"NO MORE MOVES", 1); // Continue in 4 seconds TheGame.count = 240; break; } } // Main game loop void game_loop() { char bx = 0xff, cx; switch (TheGame.state) { case GS_READY: if (!--TheGame.count) game_state(GS_PLAYER_MOVE); break; case GS_PLAYER_MOVE: // Current selection cx = TheGame.posx; // Check for key pressed if (kbhit()) { // Get key char ch = getch(); // Numbers 1 to 7 drop immediate if (ch >= '1' && ch <= '7') bx = cx = ch - '1'; } else { // Check Joystick joy_poll(0); // Move cursor if (joyx[0] < 0 && cx > 0) cx--; else if (joyx[0] > 0 && cx < 6) cx++; else if(joyb[0]) bx = cx; } // Move selection piece column_select_anim(&cwm, TheGame.posx, cx, 7); TheGame.posx = cx; if (bx != 0xff) { // Drop piece into board if (board_drop(bx, true)) { // Remember move TheGame.moves[TheGame.step++] = bx; // Play drop animation item_drop_anim(&cwm, bx, board.ffree[bx], 7); // Show new state of the board board_draw_main(&cwm); spr_show(0, false); // Check for win condition if (board_eval(true) == 32000) game_state(GS_PLAYER_WIN); else game_state(GS_COMPUTER_MOVE); } } break; case GS_COMPUTER_MOVE: // Check for opening move or calculate next move bx = board_opening(TheGame.step, TheGame.moves); if (bx == 0xff) bx = board_check(0, -32767, 32767, false); // Drop piece into board if (board_drop(bx, false)) { // Play drop animation item_drop_anim(&cwm, bx, board.ffree[bx], 2); // Show new state of the board board_draw_main(&cwm); spr_show(0, false); // Check for loss or draw condition if (board_eval(false) == 32000) game_state(GS_COMPUTER_WIN); else if (TheGame.step == 21) game_state(GS_GAME_OVER); else game_state(GS_PLAYER_MOVE); } break; case GS_PLAYER_WIN: case GS_COMPUTER_WIN: case GS_GAME_OVER: if (!--TheGame.count) game_state(GS_READY); break; } } int main(void) { mmap_trampoline(); // Install character set mmap_set(MMAP_RAM); memcpy(Font, charset, 2048); memcpy(Sprites, spriteset, 256); mmap_set(MMAP_NO_BASIC); // Switch screen vic_setmode(VICM_TEXT_MC, Screen, Font); spr_init(Screen); // Change colors vic.color_border = VCOL_BLUE; vic.color_back = VCOL_BLACK; vic.color_back1 = VCOL_BLUE; vic.color_back2 = VCOL_WHITE; vic.spr_mcolor0 = VCOL_DARK_GREY; vic.spr_mcolor1 = VCOL_WHITE; // Clear screen memset(Screen, 96, 1000); memset(Color, 15, 1000); // Prepare char wins cwin_init(&cws, Screen, 5, 0, 30, 1); cwin_init(&cwm, Screen, 2, 3, 28, 19); cwin_init(&cw, Screen, 30, 6, 8, 7); cwin_init(&cwt, Screen, 30, 15, 8, 7); // Init AI ai_init(); game_state(GS_READY); for(;;) { game_loop(); vic_waitFrame(); } return 0; }