Checker

Game logic

Game Logic code
def init_game(game_settings):
    init_board = [
            -1, 1, -1, 1, -1, 1, -1, 1, 
            1, -1, 1, -1, 1, -1, 1, -1, 
            -1, 1, -1, 1, -1, 1, -1, 1, 
            -1, -1, -1, -1, -1, -1, -1, -1, 
            -1, -1, -1, -1, -1, -1, -1, -1, 
            0, -1, 0, -1, 0, -1, 0, -1, 
            -1, 0, -1, 0, -1, 0, -1, 0, 
            0, -1, 0, -1, 0, -1, 0, -1
        ]
    return {
        "player_turn_idx": 0,
        "board": init_board,
        "valid_moves": _get_all_valid_moves(init_board, 0),
        "no_piece_taken_count": 0
    }

def move(state, start, end):
    board = state['board']
    if start < 0 or start >= 64:
        raise InvalidActionError("Starting position is out of bound")
    if end < 0 or end >= 64:
        raise InvalidActionError("Ending position is out of bound")
    if not _is_empty(board, end):
        raise InvalidActionError("Ending position is not empty")
    valid_moves = state['valid_moves']
    valid_move_tuples = [(move['start'], move['end']) for move in valid_moves]
    if (start, end) not in valid_move_tuples:
        raise InvalidActionError("Not a valid move")

    player_idx = state['player_turn_idx']
    is_piece_taken = False
    if abs(end - start) > 10: # jumped over something
        state['board'][end] = state['board'][start]
        state['board'][start] = -1
        state['board'][int((start + end)/2)] = -1
        is_piece_taken = True
    else: # did not jump over something
        state['board'][end] = state['board'][start]
        state['board'][start] = -1
        state['player_turn_idx'] = (player_idx + 1) % 2
    if player_idx == 0:
        if not _is_king(state['board'], end) and end <= 7:
            state['board'][end] += 2
    else:
        if not _is_king(state['board'], end) and end >= 56:
            state['board'][end] += 2
    
    if is_piece_taken:
        moves_after_jumping = _valid_move(state['board'], end, player_idx)
        more_jump_moves = []
        for next_move_end in moves_after_jumping:
            if _is_jump_move(next_move_end, end):
                more_jump_moves.append({
                    'start': end,
                    'end': next_move_end
                    })
        if len(more_jump_moves) > 0:
            state['valid_moves'] = more_jump_moves
        else:
            state['player_turn_idx'] = (player_idx + 1) % 2
            state['valid_moves'] = _get_all_valid_moves(state['board'], state['player_turn_idx'])
        state['no_piece_taken_count'] = 0
    else:
        state['no_piece_taken_count'] += 1
        state['valid_moves'] = _get_all_valid_moves(state['board'], state['player_turn_idx'])
        
    return state

def get_game_result(state):
    if len(state['valid_moves']) == 0:
        return {
            "game_result": "Winner",
            "winner_idx": 0 if state['player_turn_idx'] == 1 else 0
        }
    if state['no_piece_taken_count'] >= 40:
        return {
            "game_result": "Draw"
        }
    return {
            "game_result": "NoWinnerYet"
        }

def _is_player1_piece(board, index):
    return board[index] == 0 or board[index] == 2

def _is_player2_piece(board, index):
    return board[index] == 1 or board[index] == 3

def _is_empty(board, index):
    return board[index] == -1

def _is_king(board, index):
    return board[index] >= 2

def _index_to_row_column(index):
    return (int(index / 8), index % 8)

def _to_index(row, column):
    return row * 8 + column

def _is_jump_move(start, end):
    return abs(start - end) > 10

def _valid_move(board, start, player_idx):
    if _is_king(board, start):
        directions = [(1, 1), (1, -1), (-1, 1), (-1, -1)]
    elif player_idx == 0:
        directions = [(-1, 1), (-1, -1)]
    else:
        directions = [(1, 1), (1, -1)]

    def _within_bound(row, column):
        return row >= 0 and column >= 0 and row < 8 and column < 8

    def is_opponent(index):
        if player_idx == 0:
            return _is_player2_piece(board, index)
        else:
            return _is_player1_piece(board, index)

    start_r, start_c = _index_to_row_column(start)
    valid_ends = []
    for d_r, d_c in directions:
        if _within_bound(start_r + d_r, start_c + d_c):
            end = _to_index(start_r + d_r, start_c + d_c)
            if _is_empty(board, end):
                valid_ends.append(end)
        if _within_bound(start_r + d_r*2, start_c + d_c*2):
            mid = _to_index(start_r + d_r, start_c + d_c)
            end = _to_index(start_r + d_r*2, start_c + d_c*2)
            if _is_empty(board, end) and is_opponent(mid):
                valid_ends.append(end)
    return valid_ends

def _get_all_valid_moves(board, player_idx):
    is_player1 = (player_idx == 0)
    is_self_func = _is_player1_piece if is_player1 else _is_player2_piece
    moves = []
    for i in range(64):
        if is_self_func(board, i):
            valid_ends = _valid_move(board, i, player_idx)
            for end in valid_ends:
                moves.append({
                    'start': i,
                    'end': end
                    })
    jump_moves = []
    for move in moves:
        if _is_jump_move(move['start'], move['end']):
            jump_moves.append(move)
    if len(jump_moves) > 0:
        return jump_moves
    return moves

Game state schema

message State {
  string user1 = 1;
  string user2 = 2;
  string user_turn = 3;
  repeated int32 board = 4;
  repeated ValidMove valid_moves = 5;
  int32 no_piece_taken_count = 6;
}
message ValidMove {
  int32 start = 1;
  int32 end = 2;
}

Action schema

move(int start, int end)

Start building now

Last updated