Ian Ozsvald :: ian12.py

This bot has played 710 games (327 wins / 10 draws / 373 losses).

Play against this bot as X or as O.

These bots have done best aganist ian12.py...

Bot P W D L Points Size From beginner One hit wonder Goldfish
Jon Bannister : : anybot_r282.py (v5) 10 10 0 0 10 974 False False True
Mac Chapman : : lines.py 10 10 0 0 10 893 False False False
Robert Seaman : : not_minimax.py (v25) 10 10 0 0 10 3501 False False True
Tom Campbell : : winblockprevent.py (v5) 10 5 2 3 2 321 False False True
Daniel Patrick : : error_unable_to_connect4.py (v2) 10 5 0 5 0 757 False False True

...and these bots have done worst

Bot P W D L Points Size From beginner One hit wonder Goldfish
Joe Jordan : : jbot2.py (v4) 10 0 0 10 -10 719 False False False
Ronan Murphy : : loser.py 10 0 0 10 -10 105 False False True
Luke Spademan : : lossingbot.py (v3) 10 0 0 10 -10 129 False False True
house : : opportunist.py 10 0 0 10 -10 None None None None
Peter Inglesby : : random.py 10 0 0 10 -10 26 False True True

All standings against ian12.py

The code

      

# avoid losing, take winning move
# search for row 0 trivial trap and block centremost
# search for 1 ply high-scoring move for me (2 cell reachouts all directions)
# apply best move from 1 ply search

import copy
import random
from botany_connectfour import game
import botany_core
import random
import math
from botany_connectfour.game import NROWS, NCOLS, EMPTY


def get_row(col, board):
    for row in range(NROWS):
        if board[col][row] == EMPTY:
            return row

def token_in_cell(col, row, token, board):
    if col >= 0 and col < NCOLS and row >= 0 and row < NROWS:
        return board[col][row] == token
    return False


def evaluate_move(col, token, board):
    row = get_row(col, board)
    # row is where the piece could be placed
    tl = token_in_cell(col-1, row+1, token, board)
    tl2 = token_in_cell(col-2, row+2, token, board)
    br = token_in_cell(col+1, row-1, token, board)
    br2 = token_in_cell(col+2, row-2, token, board)
    bl = token_in_cell(col-1, row-1, token, board)
    bl2 = token_in_cell(col-2, row-2, token, board)
    tr = token_in_cell(col+1, row+1, token, board)
    tr2 = token_in_cell(col+2, row+2, token, board)
    l = token_in_cell(col-1, row, token, board)
    l2 = token_in_cell(col-2, row, token, board)
    r = token_in_cell(col+1, row, token, board)
    r2 = token_in_cell(col+2, row, token, board)
    b = token_in_cell(col, row-1, token, board)
    b2 = token_in_cell(col, row-2, token, board)
    #print(tl, br, bl, tr, l, r, b)
    diag1 = math.pow(tl+tl2+br+br2, 2)
    diag2 = math.pow(bl+bl2+tr+tr2, 2)
    level = math.pow(l+l2+r+r2, 2)
    bottom = math.pow(b+b2, 2)
    return diag1+diag2+level+bottom

def undo_move(board, col):
    """Update `board`, by dropping `token` in `col`"""
    for row in range(game.NROWS):
        if board[col][row] == game.EMPTY:
            board[col][row-1] = game.EMPTY
            break
    # if we got here then we hit the top row
    board[col][game.NROWS-1] = game.EMPTY

def get_next_move(board, token):
    available_moves = game.available_moves(board)
    better_available_moves = []

    if token == 'X':
        other_token = 'O'
    else:
        other_token = 'X'

    # check if we can win or might avoid losing
    board1 = copy.deepcopy(board) # +3,000 opcodes
    for col in available_moves:
        game.make_move(board1, col, token)
        if game.check_winner(board1) is not None:
            print("winning me:", col)
            return col
        undo_move(board1, col)

        game.make_move(board1, col, other_token) # + 100 opcodes
        if game.check_winner(board1) is not None: # 1,400 opcodes
            print("winning them, blocking with:", col)
            return col
        undo_move(board1, col)

    # check for simple traps on row 0
    for col in range(NCOLS-4, -1, -1):
        print("col start", col)
        n_empty = 0
        first_empty_col = None
        n_other_token = 0
        for col_check in range(col, col+4):
            cell = board[col_check][0]
            if cell == EMPTY:
                n_empty += 1
                if first_empty_col is None:
                    first_empty_col = col_check
            if cell == other_token:
                n_other_token += 1
        print(n_empty, n_other_token)
        if n_empty == 2 and n_other_token == 2:
            print("TRAP!", first_empty_col)
            return first_empty_col


    col_preferences = [0, 0.1, 0.2, 0.3, 0.2, 0.1, 0.0]

    better_moves_scored = []
    for possible_move in available_moves:
        score = evaluate_move(possible_move, token, board)
        score += col_preferences[possible_move]
        print(f"Moving {token} to {possible_move} has score {score}")

        better_moves_scored.append((possible_move, score))
    better_moves_scored = sorted(better_moves_scored, key=lambda p: p[1], reverse=True)
    print(better_moves_scored)
    move = better_moves_scored[0]

    print("op:", botany_core.tracer.get_opcode_count(), botany_core.tracer.get_opcode_limit())
    return move