Generals

Game logic

Game Logic code
import random

def init_game(game_settings):
    width = 10
    height = 10
    mountain_count = 10
    tower_count = 2

    board_list = []
    for i in range(mountain_count):
        board_list.append(_build_tile('mountain', 0))
    for i in range(tower_count):
        board_list.append(_build_tile('tower', 40))
    board_list.append(_build_tile('king', 1, 0))
    board_list.append(_build_tile('king', 1, 1))
    for i in range(width*height - len(board_list)):
        board_list.append(_build_tile('regular', 0, None))

    board = _make_2d_board(board_list, width, height)
    return {
        "player_turn_idx": 0,
        "player1_total_count": 1,
        "player2_total_count": 1,
        "board": board,
        "round": 0,
        "board_width": width,
        "board_height": height
    }

def _build_tile(tile_type, count, tile_player_idx = None, is_visible = True):
    return {
        'type': tile_type,
        'tile_player_idx': tile_player_idx,
        'count': count,
        'is_visible': is_visible
    }

def _make_2d_board(board_list, width, height):
    random.shuffle(board_list)
    index = 0
    board = []
    for r in range(height):
        row = []
        for c in range(width):
            row.append(board_list[index])
            index += 1
        board.append(row)
    return board

def _is_out_of_bound(row, column, board):
    if row < 0 or row >= len(board):
        return True
    if column < 0 or column >= len(board[row]):
        return  True
    return False

def move(state, row, column, direction, move_half):
    board = state['board']
    if _is_out_of_bound(row, column, board):
        raise InvalidActionError("Start position is out of bound")
    start_piece = board[row][column]
    player_idx = state['player_turn_idx']
    if start_piece["tile_player_idx"] != player_idx:
        raise InvalidActionError("Cannot move from a tile you do not control")

    end_row = row
    end_column = column
    if direction == "UP":
        end_row -= 1
        if end_row < 0:
            raise InvalidActionError("Moving out of bound")
    elif direction == "DOWN":
        end_row += 1
        if end_row >= len(board):
            raise InvalidActionError("Moving out of bound")
    elif direction == "LEFT":
        end_column -= 1
        if end_column < 0:
            raise InvalidActionError("Moving out of bound")
    elif direction == "RIGHT":
        end_column += 1
        if end_column >= len(board[row]):
            raise InvalidActionError("Moving out of bound")
    else:
        raise InvalidActionError("Direction should be UP, DOWN, LEFT, or RIGHT")

    
    end_piece = board[end_row][end_column]
    number_to_move = int(start_piece["count"]/2) if move_half else start_piece["count"] - 1
    number_to_move = max(number_to_move, 0)

    if end_piece["type"] != "mountain":
        start_piece["count"] -= number_to_move
        if end_piece["tile_player_idx"] == None:
            end_piece["count"] -= number_to_move
        elif end_piece["tile_player_idx"] == start_piece["tile_player_idx"]:
            end_piece["count"] += number_to_move
        else:
            end_piece["count"] -= number_to_move

        if end_piece["count"] < 0:
            end_piece["count"] = -end_piece["count"]
            end_piece["tile_player_idx"] = player_idx

    state = _auto_update(state)
    state = _recompute_count(state)
    return state

def _recompute_count(state):
    player1_count = 0
    player2_count = 0
    for row in state["board"]:
        for piece in row:
            if piece["tile_player_idx"] == 0:
                player1_count += piece["count"]
            if piece["tile_player_idx"] == 1:
                player2_count += piece["count"]
    state["player1_total_count"] = player1_count
    state["player2_total_count"] = player2_count
    return state

def do_nothing(state):
    state = _auto_update(state)
    state = _recompute_count(state)
    return state

def _auto_update(state):
    player_idx = state['player_turn_idx']
    if player_idx == 0:
        state["player_turn_idx"] = 1
        return state
    state["player_turn_idx"] = 0
    

    if state["round"] % 25 == 0:
        for row in state["board"]:
            for piece in row:
                if piece["tile_player_idx"] != None:
                    piece["count"] += 1 
    else:
        for row in state["board"]:
            for piece in row:
                piece_type = piece["type"]
                if (piece_type == "tower" or piece_type == "king") and piece["tile_player_idx"] != None:
                    piece["count"] += 1 

    state["round"] += 1
    return state

def get_game_result(state):
    has_player1 = False
    has_player2 = False
    for row in state["board"]:
        for piece in row:
            if piece["tile_player_idx"] == 0:
                if piece["type"] == "king":
                    has_player1 = True
            if piece["tile_player_idx"] == 1:
                if piece["type"] == "king":
                    has_player2 = True
    if has_player1 and not has_player2:
        return {
            "game_result": "Winner",
            "winner_idx": 0
        }
    if has_player2 and not has_player1:
        return {
            "game_result": "Winner",
            "winner_idx": 1
        }
    if state["round"] > 500:
        if state["player1_total_count"] == state["player2_total_count"]:
            return {
                "game_result": "Draw"
            }
        elif state["player1_total_count"] > state["player2_total_count"]:
            return {
                "game_result": "Winner",
                "winner_idx": 0
            }
        else:
            return {
                "game_result": "Winner",
                "winner_idx": 1
            }
    return {
            "game_result": "NoWinnerYet"
        }

def _get_visible(state, player_idx):
    initial_visible = set()
    visible = set()
    board = state["board"]
    for row_idx, row in enumerate(board):
        for column_idx, piece in enumerate(row):
            if piece["tile_player_idx"] == player_idx:
                visible.add((row_idx, column_idx))
                initial_visible.add((row_idx, column_idx))
    for r, c in initial_visible:
        for i in [-1, 0, 1]:
            for j in [-1, 0, 1]:
                if not _is_out_of_bound(r + i, c + j, board):
                    visible.add((r + i, c + j))
    return visible


def get_player_states(state):
    player_states = []
    for i in range(2):
        visible_coordinates = _get_visible(state, i)
        player_board = []
        for row_idx, row in enumerate(state["board"]):
            player_board.append([])
            for column_idx, piece in enumerate(row):
                if (row_idx, column_idx) in visible_coordinates:
                    player_board[row_idx].append(piece)
                elif piece["type"] == "mountain":
                    player_board[row_idx].append(piece)
                else:
                    player_board[row_idx].append(_build_tile('regular', 0, None, False))
        
        player_states.append({
            "player_turn_idx": state["player_turn_idx"],
            "board": player_board,
            "round": state["round"],
            "board_width": state["board_width"],
            "board_height": state["board_height"],
            "player1_total_count": state["player1_total_count"],
            "player2_total_count": state["player2_total_count"]
        })
    return player_states

Game state schema

message State {
  int32 player_turn_idx = 1;
  int32 player1_total_count = 2;
  int32 player2_total_count = 3;
  repeated Tile board = 4 [packed = true];
  int32 round = 5;
  int32 board_width = 6;
  int32 board_height = 7;
}
message Tile {
    string tile_type = 1;
    int32 tile_player_idx = 2;
    int32 count = 3;
    bool is_visible = 4;
}

Action schema

move(int row, int column, string direction, bool move_half)
do_nothing()

Start building now

Last updated