void makeMove(Piece *p1, Piece *p2) { p1->mode = NORMAL; moves[n_moves++] = *p1; p1->value = BLANK; p2->mode = NORMAL; moves[n_moves++] = *p2; p2->value = BLANK; updatePiece(p1); updatePiece(p2); n_pairs_remaining--; giveHelp(0); }
void undoMove() { if(n_moves) { Piece *p1 = &moves[--n_moves]; Piece *p2 = &moves[--n_moves]; board[p1->x][p1->y][p1->z].value = p1->value; board[p2->x][p2->y][p2->z].value = p2->value; selectPiece(NULL); updatePiece(p1); updatePiece(p2); n_pairs_remaining++; } }
void Position::undoMove() { Move theMove; theMove = movesMade.list[movesMade.totalMoves - 1]; toMove = -toMove; //Move piece back board[theMove.fromSpace] = theMove.piece; updatePiece(theMove.toSpace, theMove.fromSpace, toMove); //If a piece was captured, put it back, you! if (theMove.capture != 0) { //If it was an en passant capture if (theMove.enPassant) { board[theMove.toSpace + 10*toMove] = -PAWN*toMove; addPiece(theMove.toSpace +10*toMove, -toMove); board[theMove.toSpace] = EMPTY; } else { board[theMove.toSpace] = theMove.capture; addPiece(theMove.toSpace, -toMove); } } else board[theMove.toSpace] = EMPTY; //reupdate king positions if (abs(theMove.piece) == KING){ if (theMove.piece == KING) whiteKing = theMove.fromSpace; else blackKing = theMove.fromSpace; } if (theMove.castle != 0) { if (toMove == WHITE) { if (theMove.castle == 1) { board[96] = EMPTY; board[98] = ROOK; updatePiece(96, 98, WHITE); } else { board[94] = EMPTY; board[91] = ROOK; updatePiece(94, 91, WHITE); } } else { if (theMove.castle == 1) { board[26] = EMPTY; board[28] = -ROOK; updatePiece(26, 28, BLACK); } else { board[24] = EMPTY; board[21] = -ROOK; updatePiece(24, 21, BLACK); } } } if (toMove == BLACK) totalMoves--; halfMoves--; castleWK = WKhistory[halfMoves]; castleBK = BKhistory[halfMoves]; castleWQ = WQhistory[halfMoves]; castleBQ = BQhistory[halfMoves]; enPassant = enPassantHistory[halfMoves]; fiftyMove = fiftyMoveHistory[halfMoves]; hash = hashHistory[halfMoves]; movesMade.remove_last(); }
bool Position::doMove(Move &theMove) { //save history updateHistory(); //First up: move the piece on the board, update piecelist board[theMove.toSpace] = theMove.piece; board[theMove.fromSpace] = EMPTY; updatePiece(theMove.fromSpace, theMove.toSpace, toMove); updatePieceHash(theMove.fromSpace, theMove.toSpace, theMove.piece); //If it is a capture, let's delete the dead guy from the proper piecelist if (theMove.capture != 0) { //For enpassant captures if(theMove.enPassant == true) { removePiece(enPassant + 10*toMove, -toMove); updatePieceHash(enPassant + 10*toMove, 0, theMove.capture); board[enPassant + 10*toMove] = EMPTY; } else { removePiece(theMove.toSpace, -toMove); updatePieceHash(theMove.toSpace, 0, theMove.capture); } //If a rook is captured, update castle info. if(abs(theMove.capture) == ROOK) { if (castleWK && theMove.toSpace == 98) { castleWK = false; hash = hash ^ castleKeys[0]; } else if (castleWQ && theMove.toSpace == 91) { castleWQ = false; hash = hash ^ castleKeys[1]; } else if (castleBK && theMove.toSpace == 28) { castleBK = false; hash = hash ^ castleKeys[2]; } else if (castleBQ && theMove.toSpace == 21) { hash = hash ^ castleKeys[3]; castleBQ = false; } } } //Speaking of en passant captures... if (enPassant != 0) hash = hash ^ enpassantKeys[enPassant%10 - 1]; if (theMove.jump) { enPassant = theMove.fromSpace - 10*toMove; hash = hash ^ enpassantKeys[enPassant%10 - 1]; } else enPassant = 0; //For promotions, we must bring a piece to LIFE! if (theMove.promotion != 0) { board[theMove.toSpace] = theMove.promotion*toMove; updatePieceHash(0, theMove.toSpace, theMove.promotion*toMove); updatePieceHash(theMove.toSpace, 0, theMove.piece); } //Castling. Hooray. Move the rooks, update piecelists. if (theMove.castle != 0) { if (theMove.castle == 1) { if (toMove == WHITE) { board[98] = EMPTY; board[96] = ROOK; updatePiece(98, 96, WHITE); updatePieceHash(98, 96, ROOK); } else { board[28] = EMPTY; board[26] = -ROOK; updatePiece(28, 26, BLACK); updatePieceHash(28, 26, -ROOK); } } else { if (toMove == WHITE) { board[91] = EMPTY; board[94] = ROOK; updatePiece(91, 94, WHITE); updatePieceHash(91, 94, ROOK); } else { board[21] = EMPTY; board[24] = -ROOK; updatePiece(21, 24, BLACK); updatePieceHash(21, 24, -ROOK); } } } //Update king locations if (abs(theMove.piece) == KING) { if (toMove == WHITE) { whiteKing = theMove.toSpace; if (castleWK) { castleWK = false; hash = hash ^ castleKeys[0]; } if (castleWQ) { castleWQ = false; hash = hash ^ castleKeys[1]; } } else { blackKing = theMove.toSpace; if (castleBK) { castleBK = false; hash = hash ^ castleKeys[2]; } if (castleBQ) { castleBQ = false; hash = hash ^ castleKeys[3]; } } } //Update castling rights for rook moves if (theMove.piece == ROOK) { if (theMove.fromSpace == 91 && castleWQ) { castleWQ = false; hash = hash ^ castleKeys[1]; } else if (theMove.fromSpace == 98 && castleWK){ castleWK = false; hash = hash ^ castleKeys[0]; } } else if (theMove.piece == -ROOK) { if (theMove.fromSpace == 21 && castleBQ){ castleBQ = false; hash = hash ^ castleKeys[3]; } else if (theMove.fromSpace == 28 && castleBK){ castleBK = false; hash = hash ^ castleKeys[2]; } } //Adjust fifty move rule counter if (abs(theMove.piece) == PAWN || theMove.capture != 0) fiftyMove = 0; else fiftyMove++; if (toMove == BLACK) totalMoves++; halfMoves++; bool check = inCheck(); toMove = -toMove; hash = hash ^ blackKey; //update moves made and history movesMade.add(theMove); return check; }
int main(int argc, char *argv[]) { { int i; if((argc - 1) % 2) goto help; for(i = 1; i < argc - 1; i += 2) if(!editing && (!strcmp(argv[i], "-e") || !strcmp(argv[i], "--edit"))) editing = argv[i+1]; else if(!layout_title[0] && (!strcmp(argv[i], "-t") || !strcmp(argv[i], "--title"))) { bzero(layout_title, 55); strncpy(layout_title, argv[i+1], 54); } else { help: printf("usage: mahjong [--edit <layout> [--title <layout title>]]\n"); return 0; } } srand(time(NULL)); allegro_init(); { int x, y, z; for(x = 16; x--;) for(y = 9; y--;) for(z = 3; z--;) pieceInit(board[x][y][z], x, y, z); } set_color_depth(SCREEN_DEPTH); if(set_gfx_mode(GFX_AUTODETECT_WINDOWED, SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_WIDTH*2, SCREEN_HEIGHT) < 0) { fprintf(stderr, "fatal: %s\n", allegro_error); exit(1); } #ifdef ALLEGRO_WINDOWS set_display_switch_callback(SWITCH_IN, update); #endif left_view = create_sub_bitmap(screen, 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT); right_view = create_sub_bitmap(screen, SCREEN_WIDTH, 0, SCREEN_WIDTH, SCREEN_HEIGHT); // init colors BACKGROUND_COLOR = makecol32(0x2F, 0x5F, 0x2F); // soft green SELECTED_COLOR = makecol32(0x00, 0xDF, 0x00); // green OLD_SELECTED_COLOR = makecol32(0x00, 0xBF, 0xBF); // cyan // load data { DATAFILE *data = load_datafile("#"); new_game = data[NEW_GAME_BMP].dat; undo = data[UNDO_BMP].dat; help = data[HELP_BMP].dat; quit = data[QUIT_BMP].dat; tile = data[TILE_BMP].dat; number[ 0] = data[BLACK1_BMP].dat; number[ 2] = data[BLACK2_BMP].dat; number[ 4] = data[BLACK3_BMP].dat; number[ 6] = data[BLACK4_BMP].dat; number[ 8] = data[BLACK5_BMP].dat; number[10] = data[BLACK6_BMP].dat; number[12] = data[BLACK7_BMP].dat; number[14] = data[BLACK8_BMP].dat; number[16] = data[BLACK9_BMP].dat; number[ 1] = data[RED1_BMP].dat; number[ 3] = data[RED2_BMP].dat; number[ 5] = data[RED3_BMP].dat; number[ 7] = data[RED4_BMP].dat; number[ 9] = data[RED5_BMP].dat; number[11] = data[RED6_BMP].dat; number[13] = data[RED7_BMP].dat; number[15] = data[RED8_BMP].dat; number[17] = data[RED9_BMP].dat; suit[0] = data[SPADE_BMP].dat; suit[1] = data[CLUB_BMP].dat; suit[2] = data[DIAMOND_BMP].dat; suit[3] = data[HEART_BMP].dat; layouts[0] = data[BLOCK_LYT].dat; layouts[1] = data[FLAT_LYT].dat; layouts[2] = data[FROGGER_LYT].dat; layouts[3] = data[PRECIOUS_LYT].dat; layouts[4] = data[PTRAD_LYT].dat; layouts[5] = data[PYRAMID_LYT].dat; layouts[6] = data[STEPS_LYT].dat; layouts[7] = data[THETA_LYT].dat; } scroll_screen(SCREEN_WIDTH, 0); current_view = right_view; install_timer(); install_mouse(); install_keyboard(); show_mouse(current_view); text_mode(BACKGROUND_COLOR); if(!editing) { defaultLayout(); if(alert("Our Own Version of Mahjong Solitaire, v0.1.4", NULL, "Copyright (c) 2001 Eric Mulvaney, Michelle Bondy", "Play", "Edit", 0, 0) == 2 && file_select_ex("Please select layout file to edit:", path, "lyt", PATH_LENGTH-1, OLD_FILESEL_WIDTH, OLD_FILESEL_HEIGHT)) { int x, y, z; editing = path; for(x = 16; x--;) for(y = 9; y--;) for(z = 3; z--;) board[x][y][z].value = EMPTY; } } mouse_callback = editing ? mouse_handler_for_editing : mouse_handler; if(editing) { Layout data; FILE *file = fopen(editing, "r"); if(file) { if(fread(&data, sizeof(Layout), 1, file)) { int x, y, z; if(!layout_title[0]) memcpy(layout_title, data.title, 55); for(x = 16; x--;) for(y = 9; y--;) for(z = (data.board[x][y] > 3) ? 3 : data.board[x][y]; z--;) { board[x][y][z].value = BLANK; if(!--n_pieces_left) goto skip; } } skip: fclose(file); } update(); } click_ready = 0; while(1) // game loop { if(click_ready) { int x = click_x - (BOARD_XOFF - 2 * EDGE_WIDTH ); int y = click_y - (BOARD_YOFF - 2 * EDGE_HEIGHT); int z; for(z = 3; x > 0 && y > 0 && z--; x -= EDGE_WIDTH, y -= EDGE_HEIGHT) { int i = x / FACE_WIDTH; int j = y / FACE_HEIGHT; if(i >= 16 || j >= 9) continue; if(editing) { if(click_ready == 1 && board[i][j][z].value == EMPTY) { if((z == 0 || board[i][j][z-1].value != EMPTY) && n_pieces_left) { n_pieces_left--; board[i][j][z].value = BLANK; updatePiece(&board[i][j][z]); goto event_handled; } } else if(click_ready == 2 && board[i][j][z].value != EMPTY) { if(z == 2 || board[i][j][z+1].value == EMPTY) { board[i][j][z].value = EMPTY; n_pieces_left++; updatePiece(&board[i][j][z]); goto event_handled; } } } else if(selectPiece(&board[i][j][z])) { if(!n_pairs_remaining) { if(current_view != left_view) update(); if(alert("Congratulations! You won!", "Play another?", NULL, "Yes", "No", 0, 0) == 1) newGame(); else return 0; } goto event_handled; } } if(click_y < BUTTON_YOFF + BUTTON_HEIGHT && click_y > BUTTON_YOFF) { if(editing) { if(click_x > NEW_GAME_BUTTON_X1 && click_x < NEW_GAME_BUTTON_X2) { if(n_pieces_left == 144) goto event_handled; if(current_view != left_view) update(); if(alert("Are you sure you want to clear the current Layout?", NULL, NULL, "Yes", "No", 0, 0) == 1) { int x, y, z; for(x = 16; x--;) for(y = 9; y--;) for(z = 3; z--;) board[x][y][z].value = EMPTY; n_pieces_left = 144; update(); } } else if(click_x > QUIT_BUTTON_X1 && click_x < QUIT_BUTTON_X2) { int ync; if(current_view != left_view) update(); if(n_pieces_left) ync = alert3("WARNING: Layout is incomplete.", NULL, "Do you wish to save before exiting?", "Yes", "No", "Cancel", 0, 0, 0); else ync = alert3("Do you wish to save before exiting?", NULL, NULL, "Yes", "No", "Cancel", 0, 0, 0); if(ync == 2) return 0; else if(ync == 1) { Layout data; FILE *file; memcpy(data.title, layout_title, 55); data.complete = (n_pieces_left) ? 0 : 1; if((file = fopen(editing, "w"))) { for(x = 16; x--;) for(y = 9; y--;) { if (board[x][y][2].value == BLANK) data.board[x][y] = 3; else if(board[x][y][1].value == BLANK) data.board[x][y] = 2; else if(board[x][y][0].value == BLANK) data.board[x][y] = 1; else data.board[x][y] = 0; } if(fwrite(&data, sizeof(Layout), 1, file)) { fclose(file); return 0; } else fclose(file); } if(alert("WARNING: Save failed!", NULL, "Do you still wish to exit?", "Yes", "No", 0, 0) == 1) return 0; } } } else if(click_x > NEW_GAME_BUTTON_X1 && click_x < NEW_GAME_BUTTON_X2) newGame(); else if(click_x > UNDO_BUTTON_X1 && click_x < UNDO_BUTTON_X2) undoMove(); else if(click_x > HELP_BUTTON_X1 && click_x < HELP_BUTTON_X2) giveHelp(1); else if(click_x > QUIT_BUTTON_X1 && click_x < QUIT_BUTTON_X2) { if(current_view != left_view) update(); if(alert("Are you sure you want to quit?", NULL, NULL, "Yes", "No", 0, 0) == 1) return 0; } } event_handled: click_ready = 0; } else rest(100); } return 0; }
int selectPiece(Piece *p) { static Piece *s1 = NULL, *s2 = NULL; if(!p) // reset { if(s1) { s1->mode = NORMAL; updatePiece(s1); s1 = NULL; } if(s2) { s2->mode = NORMAL; updatePiece(s2); s2 = NULL; } return 0; } if(p->value >= EMPTY || (p->x > 0 && p->x < 15 && board[p->x-1][p->y][p->z].value < EMPTY && board[p->x+1][p->y][p->z].value < EMPTY) || (p->z < 2 && board[p->x][p->y][p->z+1].value < EMPTY)) return 0; if(!s1) { s1 = p; s1->mode = SELECTED; updatePiece(s1); } else if(p == s1) { s1->mode = NORMAL; if(s2) { s2->mode = SELECTED; updatePiece(s2); } updatePiece(s1); s1 = s2; s2 = NULL; } else if(pieceMatch(*p, *s1)) { if(s2) { s2->mode = SELECTED; updatePiece(s2); } makeMove(p, s1); s1 = s2; s2 = NULL; } else { p->mode = SELECTED; s1->mode = OLD_SELECTED; if(s2 && p != s2) { s2->mode = NORMAL; updatePiece(s2); } updatePiece(p); updatePiece(s1); s2 = s1; s1 = p; } return 1; }