Skip to content

Commit

Permalink
Merge pull request #107 from achistyakov/dev
Browse files Browse the repository at this point in the history
update reversi
  • Loading branch information
xMasterX authored May 24, 2024
2 parents ccff085 + d388144 commit b37daf6
Show file tree
Hide file tree
Showing 4 changed files with 115 additions and 37 deletions.
4 changes: 2 additions & 2 deletions apps_source_code/reversi/application.fam
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,13 @@ App(
requires=[
"gui",
],
stack_size=1 * 1024,
stack_size=2 * 1024,
order=90,
fap_icon="game_reversi.png",
fap_category="Games",
fap_icon_assets_symbol="game_reversi",
fap_author="@dimat",
fap_weburl="https://github.com/zyuhel/flipperzero-racegame",
fap_version="1.2",
fap_version="1.3",
fap_description="Reversi game, the game controls should be intuitive. Longs press on OK opens the menu to start a new game.",
)
11 changes: 2 additions & 9 deletions apps_source_code/reversi/game_reversi.c
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,7 @@
#define FRAME_TOP 3
#define FRAME_CELL_SIZE 7

#define SAVING_DIRECTORY "/ext/apps/Games"
#define SAVING_FILENAME SAVING_DIRECTORY "/game_reversi.save"
#define SAVING_FILENAME APP_DATA_PATH("reversi.save")

typedef enum { AppScreenGame, AppScreenMenu } AppScreen;

Expand Down Expand Up @@ -188,12 +187,6 @@ bool load_game(GameState* game_state) {
void save_game(const GameState* game_state) {
Storage* storage = furi_record_open(RECORD_STORAGE);

if(storage_common_stat(storage, SAVING_DIRECTORY, NULL) == FSE_NOT_EXIST) {
if(!storage_simply_mkdir(storage, SAVING_DIRECTORY)) {
return;
}
}

File* file = storage_file_alloc(storage);
if(storage_file_open(file, SAVING_FILENAME, FSAM_WRITE, FSOM_CREATE_ALWAYS)) {
storage_file_write(file, game_state, sizeof(GameState));
Expand Down Expand Up @@ -330,7 +323,7 @@ int32_t game_reversi_app() {
furi_mutex_acquire(app_state.mutex, FuriWaitForever);
app_state.selected_menu_item = 0;
app_state.screen = AppScreenMenu;

furi_mutex_release(app_state.mutex);
view_port_update(view_port);
continue;
Expand Down
132 changes: 106 additions & 26 deletions apps_source_code/reversi/reversi.c
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,16 @@

// Psst! Most of this file was written with Copilot

const int weights[BOARD_SIZE][BOARD_SIZE] = {
{100, -10, 10, 10, 10, 10, -10, 100},
{-10, -20, -5, -5, -5, -5, -20, -10},
{10, -5, 5, 1, 1, 5, -5, 10},
{10, -5, 1, 0, 0, 1, -5, 10},
{10, -5, 1, 0, 0, 1, -5, 10},
{10, -5, 5, 1, 1, 5, -5, 10},
{-10, -20, -5, -5, -5, -5, -20, -10},
{100, -10, 10, 10, 10, 10, -10, 100}};

// Check if the move is legal by checking if it results in any opponent pieces being captured
bool is_legal_move(int8_t board[BOARD_SIZE][BOARD_SIZE], int row, int col, int player) {
if(board[row][col] != 0) return false;
Expand Down Expand Up @@ -53,52 +63,56 @@ bool has_legal_moves(int8_t board[BOARD_SIZE][BOARD_SIZE], int8_t player_color)
return false;
}

// Calculate the heuristic value of the current board. This function can
// be replaced with a more complex evaluation function that takes into
// account factors such as mobility, piece square tables, etc.
int heuristic(int8_t board[BOARD_SIZE][BOARD_SIZE]) {
int white = 0, black = 0;
int evaluate_board(int8_t board[BOARD_SIZE][BOARD_SIZE], int player) {
int score = 0;
for(int i = 0; i < BOARD_SIZE; i++) {
for(int j = 0; j < BOARD_SIZE; j++) {
if(board[i][j] == 1) white++;
if(board[i][j] == -1) black++;
if(board[i][j] == player) {
score += weights[i][j];
} else if(board[i][j] == -player) {
score -= weights[i][j];
}
}
}
return white - black;
return score;
}

// Make a move on the board and capture any opponent pieces
void make_move(GameState* state, int x, int y, int player) {
state->board[x][y] = player;
void make_move(
GameState* game_state,
int8_t board[BOARD_SIZE][BOARD_SIZE],
int x,
int y,
int player) {
board[x][y] = player;
int opponent = -player;
for(int i = -1; i <= 1; i++) {
for(int j = -1; j <= 1; j++) {
if(i == 0 && j == 0) continue;
int r = x + i, c = y + j;
if(r >= 0 && r < BOARD_SIZE && c >= 0 && c < BOARD_SIZE &&
state->board[r][c] == opponent) {
if(r >= 0 && r < BOARD_SIZE && c >= 0 && c < BOARD_SIZE && board[r][c] == opponent) {
int k = 2;
while(true) {
r += i;
c += j;
if(r < 0 || r >= BOARD_SIZE || c < 0 || c >= BOARD_SIZE) break;
if(state->board[r][c] == player) {
if(board[r][c] == player) {
r -= i;
c -= j;
while(r != x || c != y) {
state->board[r][c] = player;
board[r][c] = player;
r -= i;
c -= j;
}
break;
}
if(state->board[r][c] == 0) break;
if(board[r][c] == 0) break;
k++;
}
}
}
}
state->is_game_over = is_game_over(state->board);
game_state->is_game_over = is_game_over(game_state->board);
}

void init_game(GameState* state) {
Expand Down Expand Up @@ -136,31 +150,97 @@ void human_move(GameState* game_state) {
game_state->cursor_y,
game_state->current_player)) {
make_move(
game_state, game_state->cursor_x, game_state->cursor_y, game_state->current_player);
game_state,
game_state->board,
game_state->cursor_x,
game_state->cursor_y,
game_state->current_player);
game_state->current_player = -game_state->current_player;
}
}

int minimax(
GameState* game_state,
int8_t board[BOARD_SIZE][BOARD_SIZE],
int depth,
bool is_maximizing,
int player,
int alpha,
int beta) {
if(depth == 0 || is_game_over(board)) {
return evaluate_board(board, player);
}

if(is_maximizing) {
int max_eval = -1000000;
for(int i = 0; i < BOARD_SIZE; i++) {
for(int j = 0; j < BOARD_SIZE; j++) {
if(is_legal_move(board, i, j, player)) {
int8_t temp_board[BOARD_SIZE][BOARD_SIZE];
memcpy(temp_board, board, sizeof(temp_board));
make_move(game_state, temp_board, i, j, player);
int eval =
minimax(game_state, temp_board, depth - 1, false, -player, alpha, beta);
max_eval = max(max_eval, eval);
alpha = max(alpha, eval);
if(beta <= alpha) {
break;
}
}
}
}
return max_eval;
} else {
int min_eval = 1000000;
for(int i = 0; i < BOARD_SIZE; i++) {
for(int j = 0; j < BOARD_SIZE; j++) {
if(is_legal_move(board, i, j, -player)) {
int8_t temp_board[BOARD_SIZE][BOARD_SIZE];
memcpy(temp_board, board, sizeof(temp_board));
make_move(game_state, temp_board, i, j, -player);
int eval =
minimax(game_state, temp_board, depth - 1, true, player, alpha, beta);
min_eval = min(min_eval, eval);
beta = min(beta, eval);
if(beta <= alpha) {
break;
}
}
}
}
return min_eval;
}
}

void computer_move(GameState* game_state) {
if(game_state->current_player == game_state->human_color) {
return;
}
int best_row = -1, best_col = -1, best_score = -1000000;
for(int i = 0; i < BOARD_SIZE; i++) {
for(int j = 0; j < BOARD_SIZE; j++) {
if(!is_legal_move(game_state->board, i, j, game_state->current_player)) {
continue;
}
int score = heuristic(game_state->board);
if(score > best_score) {
best_score = score;
best_row = i;
best_col = j;
if(is_legal_move(game_state->board, i, j, game_state->current_player)) {
int8_t temp_board[BOARD_SIZE][BOARD_SIZE];
memcpy(temp_board, game_state->board, sizeof(temp_board));
make_move(game_state, temp_board, i, j, game_state->current_player);
int score = minimax(
game_state,
temp_board,
3,
false,
-game_state->current_player,
-1000000,
1000000);
if(score > best_score) {
best_score = score;
best_row = i;
best_col = j;
}
}
}
}
if(best_row != -1) {
make_move(game_state, best_row, best_col, game_state->current_player);
make_move(game_state, game_state->board, best_row, best_col, game_state->current_player);
}
if(has_legal_moves(game_state->board, game_state->human_color)) {
game_state->current_player = -game_state->current_player;
Expand Down
5 changes: 5 additions & 0 deletions apps_source_code/reversi/reversi.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,11 @@
#include <furi.h>
#include <stdbool.h>

#ifndef __cplusplus
#define max(a, b) (((a) > (b)) ? (a) : (b))
#define min(a, b) (((a) < (b)) ? (a) : (b))
#endif

#define BLACK 1
#define WHITE -1
#define BOARD_SIZE 8
Expand Down

0 comments on commit b37daf6

Please sign in to comment.