/Connect4.py
https://github.com/JoryD/Connect4AI · Python · 323 lines · 280 code · 43 blank · 0 comment · 109 complexity · 9b5eef9447aa18aafff869de4654feaf MD5 · raw file
- import sys
- import time
- from copy import deepcopy
- from random import randint, shuffle
- BOARD_WIDTH = 7
- BOARD_HEIGHT = 6
- AI_STRENGTH = 200
- cellOptions = [" ", "Y", "R"]; # empty, Yellow symbol, Red symbol
- CIRCLE_INVALID = -1
- CIRCLE_EMPTY = 0
- CIRCLE_YELLOW = 1
- CIRCLE_RED = 2
- CIRCLE_DRAW = 3
- NO_WINNER = 4
- COLORAMA = False
- CLEARABLE = False
- RUSH = True
- emptyBoard = [[0 for x in range(BOARD_WIDTH)] for y in range(BOARD_HEIGHT)]
- BX = BOARD_WIDTH - 1
- BY = BOARD_HEIGHT - 1
- ttmList = []
- BANNER = ''' _/_/_/ _/ _/ _/
- _/ _/_/ _/_/_/ _/_/_/ _/_/ _/_/_/ _/_/_/_/ _/ _/
- _/ _/ _/ _/ _/ _/ _/ _/_/_/_/ _/ _/ _/_/_/_/
- _/ _/ _/ _/ _/ _/ _/ _/ _/ _/ _/
- _/_/_/ _/_/ _/ _/ _/ _/ _/_/_/ _/_/_/ _/_/ _/ '''
- SMALL_BANNER = ''' _
- / _ ._ ._ _ __|_|_|_
- \_(_)| || |(/_(_ |_ | '''
- def Get(b, level, col):
- if col < 0 or col > BX or level < 0 or level > BY:
- return CIRCLE_INVALID
- return b[level][col]
- def Set(b, level, col, value):
- b[level][col] = value
- def ColIsFull(b, col):
- return (Get(b, BY, col) != CIRCLE_EMPTY)
- def Drop(b, col, value):
- if (ColIsFull(b,col) == CIRCLE_INVALID):
- return 0
- for level in range(0,BOARD_HEIGHT):
- if Get(b, level, col) == CIRCLE_EMPTY:
- Set(b, level, col, value)
- break
- return 1
-
-
- def printGameBoard(b):
- export = ""
- if CLEARABLE:
- os.system('cls||echo -e \\\\033c')
- if COLORAMA:
- export = export + colorama.Fore.YELLOW + SMALL_BANNER + colorama.Style.RESET_ALL + "\n"
- else:
- export = export + SMALL_BANNER + "\n"
- for level in range(BY, -1, -1):
- export = export + str(level)
- for col in range(0, BOARD_WIDTH):
- color = Get(b, level, col)
- if COLORAMA:
- if color == CIRCLE_YELLOW:
- export = export + colorama.Fore.YELLOW+"[Y]"+colorama.Style.RESET_ALL
- elif color == CIRCLE_RED:
- export = export + colorama.Fore.RED+"[R]"+colorama.Style.RESET_ALL
- else:
- export = export + "[ ]"
- else:
- export = export + "[" + str(cellOptions[color]) + "]"
- export = export + "\n"
- export = export + " "
- for col in range(0, BOARD_WIDTH):
- export = export + " " + str(col) + " "
- print(export)
- def GetWinner(b):
- empty = 0
- sp = 0
- for level in range(BY, -1, -1):
- for col in range(0, BX):
- color = Get(b, level, col)
- if (color == CIRCLE_EMPTY):
- empty = empty + 1
- continue
- directory = [[1,0],[0,1],[1,1],[-1,1]]
-
- for d in range(0, 4):
- start_col = col
- start_level = level
- while(Get(b, start_level-directory[d][1], start_col-directory[d][0]) == color):
- start_col = start_col - directory[d][0]
- start_level = start_level - directory[d][1]
-
- count = 0
- while(Get(b, start_level, start_col) == color):
- count = count + 1
- start_col = start_col + directory[d][0]
- start_level = start_level + directory[d][1]
- if (count >= 4):
- return color
- if(empty <= BOARD_HEIGHT * BOARD_WIDTH):
- return CIRCLE_EMPTY
- return CIRCLE_DRAW
- def RandomGame(b, tomove):
- for i in range(0, BOARD_HEIGHT * BOARD_WIDTH):
- potentialMoves = [x for x in range(0,BOARD_WIDTH)]
- shuffle(potentialMoves)
- for move in potentialMoves:
- if(not ColIsFull(b, move)):
- nextMove = move
- break
- if (Drop(b, nextMove, tomove)):
- if(tomove == CIRCLE_YELLOW):
- tomove = CIRCLE_RED
- else:
- tomove = CIRCLE_YELLOW
- winner = GetWinner(b)
- if (winner != CIRCLE_EMPTY):
- return winner
- return CIRCLE_DRAW
- def SuggestMove(b, tomove, simulations=AI_STRENGTH):
- best = -1
- best_ratio = 0
- if COLORAMA:
- if tomove == CIRCLE_YELLOW:
- print(colorama.Fore.YELLOW + "YELLOW IS THINKING" + colorama.Style.RESET_ALL)
- elif tomove == CIRCLE_RED:
- print(colorama.Fore.RED + "RED IS THINKING" + colorama.Style.RESET_ALL)
- for move in range(0,BX+1):
- ttm = time.time()
- if (ColIsFull(b,move)):
- continue
- won = 0
- lost = 0
- draw = 0
- print_neutral = 1
- for j in range(0, simulations):
- copy = deepcopy(b)
- Drop(copy, move, tomove);
- if (GetWinner(copy) == tomove):
- return move
- if (tomove == CIRCLE_YELLOW):
- nextPlayer = CIRCLE_RED
- else:
- nextPlayer = CIRCLE_YELLOW
- winner = RandomGame(copy, nextPlayer)
- if (winner == CIRCLE_YELLOW or winner == CIRCLE_RED):
- if (winner == tomove):
- won = won + 1
- else:
- lost = lost + 1
- else:
- draw = draw + 1
- if j == AI_STRENGTH/2 and RUSH == True:
- ratio = float(won)/(lost+won+1);
- if(ratio+0.05 <= best_ratio and best != -1):
- if COLORAMA:
- print(colorama.Fore.RED + "X" + colorama.Style.RESET_ALL, end = " ")
- print_neutral = 0
- break
- ratio = float(won)/(lost+won+1);
- if(ratio > best_ratio or best == -1):
- best = move
- best_ratio = ratio
- if COLORAMA:
- print(colorama.Fore.GREEN + "$" + colorama.Style.RESET_ALL, end = " ")
- print_neutral = 0
- if COLORAMA and print_neutral == 1:
- print(colorama.Fore.YELLOW + "?" + colorama.Style.RESET_ALL, end = " ")
- ttmList.append(time.time() - ttm)
- print("Move", move, ":", round(ratio*100,1), "draws:", draw, "ttm:", round(ttmList[-1],1), "attm:", round(sum(ttmList)/len(ttmList),1))
- return best
- def sim():
- board = deepcopy(emptyBoard)
- while(1):
- printGameBoard(board)
- if showPotentialWinner(board): return
- Drop(board,SuggestMove(board, CIRCLE_RED, AI_STRENGTH), CIRCLE_RED)
- if showPotentialWinner(board): return
- printGameBoard(board)
- Drop(board, SuggestMove(board, CIRCLE_YELLOW, AI_STRENGTH), CIRCLE_YELLOW)
-
- def singlePlayer(whoStarts=1):
- board = deepcopy(emptyBoard)
- if whoStarts == 1:
- while(1):
- printGameBoard(board)
- if showPotentialWinner(board): return
- Drop(board, getMoveFromPlayer(board), CIRCLE_RED)
- printGameBoard(board)
- if showPotentialWinner(board): return
- computer_move = SuggestMove(board, CIRCLE_YELLOW, AI_STRENGTH)
- Drop(board, computer_move, CIRCLE_YELLOW)
- elif whoStarts == 2:
- while(1):
- printGameBoard(board)
- if showPotentialWinner(board): return
- Drop(board, SuggestMove(board, CIRCLE_YELLOW, AI_STRENGTH), CIRCLE_YELLOW)
- printGameBoard(board)
- if showPotentialWinner(board): return
- Drop(board, getMoveFromPlayer(board, CIRCLE_RED), CIRCLE_RED)
- def showPotentialWinner(board):
- winner = GetWinner(board)
- if winner != CIRCLE_EMPTY and winner != CIRCLE_DRAW:
- printGameBoard(board)
- if COLORAMA:
- if winner == CIRCLE_YELLOW:
- print(colorama.Fore.YELLOW+" winner: YELLOW" + colorama.Style.RESET_ALL)
- else:
- print(colorama.Fore.RED+" winner: RED" + colorama.Style.RESET_ALL)
- return True
- else:
- print("winner: ", winner)
- return True
- elif winner == CIRCLE_DRAW:
- printGameBoard(board)
- print("DRAW!")
- return False
-
- def getMoveFromPlayer(b, color):
- potentialPlayerMove = -1
- while(potentialPlayerMove == -1):
- try:
- potentialPlayerMove = int(input("Player "+ str(color) +", state your move: "))
- except ValueError:
- print("Invalid move, try again!")
- potentialPlayerMove = -1
- continue
- if potentialPlayerMove == 9:
- return SuggestMove(b, color)
- if (potentialPlayerMove > (BX) or potentialPlayerMove < 0):
- print("Invalid move, try again!")
- potentialPlayerMove = -1
- if (potentialPlayerMove != -1 and ColIsFull(b, potentialPlayerMove) != CIRCLE_EMPTY):
- print("Invalid move, try again!")
- potentialPlayerMove = -1
- return potentialPlayerMove
- def twoPlayer():
- board = deepcopy(emptyBoard)
- while(1):
- printGameBoard(board)
- if showPotentialWinner(board): return
- Drop(board, getMoveFromPlayer(board, CIRCLE_RED), CIRCLE_RED)
- printGameBoard(board)
- potentialPlayerMove = -1
- if showPotentialWinner(board): return
- Drop(board, getMoveFromPlayer(board, CIRCLE_YELLOW), CIRCLE_YELLOW)
-
- if __name__== "__main__":
- print(BANNER)
- print("By Jory Detwiler v1.0.0.0 | https://github.com/JoryD")
- if "--easy" in sys.argv:
- AI_STRENGTH = 250
- elif "--medium" in sys.argv or "--med" in sys.argv:
- AI_STRENGTH = 500
- elif "--hard" in sys.argv:
- AI_STRENGTH = 1000
-
- elif "--insane" in sys.argv:
- AI_STRENGTH = 2500
-
- elif "--master" in sys.argv:
- AI_STRENGTH = 5000
-
- elif "--demigod" in sys.argv:
- AI_STRENGTH = 10000
-
- elif "--god" in sys.argv:
- AI_STRENGTH = 100000
-
- if "--norush" in sys.argv:
- RUSH = False
-
- elif "--rush" in sys.argv:
- RUSH = True
-
- if "--pretty" in sys.argv:
- try:
- import colorama
- colorama.init()
- COLORAMA = 1
- except ImportError:
- print("You do not have COLORAMA installed. No colors for you :(")
- print("Try `pip install colorama`, `python pip install colorama`, `python3 pip install colorama`, or maybe `pip3 install colorama`")
- if "--clearable" in sys.argv:
- import os
- CLEARABLE = 1
- if "--singleplayer" in sys.argv:
- if "--second" in sys.argv:
- singlePlayer(2)
- else:
- singlePlayer(1)
- elif "--multiplayer" in sys.argv:
- twoPlayer()
- else:
- sim()