Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 5 years ago.
Improve this question
I'm trying to create a python Connect 4 game for 2 players to play, and there is an error that I can't seem to fix. Please help. The code is supposed to randomly choose which player goes first between 1 and 2 and then get their input through the python shell. With the column number, the code should be able to place a marker different for each player onto a connect four board. Here is the code:
import random
import sys
width = 7
height = 6
player1Marker = 'X'
player2Marker = 'O'
def whoGoesFirst():
# Randomly choose the player who goes first.
if random.randint(0, 2) == 0:
return 'Player 1'
else:
return 'Player 2'
def drawBoard(board):
print()
print(' ', end='')
for x in range(1, width + 1):
print(' %s ' % x, end='')
print()
print('+---+' * (width - 1))
for y in range(height):
print('| |' + (' |' * (width - 1)))
print('|', end='')
for x in range(width):
print(' %s |' % board[x][y], end='')
print()
print('| |' + (' |' * (width - 1)))
print('+---+' + ('---+' * (width - 1)))
def getPlayer1Move(game_board):
while True:
print('Player 1: which column do you want to move on (1-7)? Type quit if you want to quit: ' % (width))
move = input()
if move.lower().startswith('q'):
sys.exit()
if move.int():
continue
move = int(move) - 1
if isValidMove(game_board, move):
return move
def getPlayer2Move(game_board):
while True:
print('Player 2: Which column do you want to move on (1-7)? Type quit if you want to quit: ' % (width))
move = input()
if move.lower().startswith('q'):
sys.exit()
if move.int():
continue
move = int(move) - 1
if isValidMove(game_board, move):
return move
def getNewBoard():
game_board = []
for x in range(width):
game_board.append([' '] * height)
return game_board
def p1makeMove(game_board, player, column):
for y in range(height -1, -1, -1):
if game_board[column][x] == ' ':
game_board[column][x] = player1
return
def p2makeMove(game_board, player, column):
for y in range(height -1, -1, -1):
if game_board[column][y] == ' ':
game_board[column][y] = player2
return
def isValidMove(game_board, move):
if move < 0 or move >= (width):
return False
if game_board[move][0] != ' ':
return False
return True
def isBoardFull(game_board):
for x in range(width):
for y in range(height):
if game_board[x][y] == ' ':
return False
return True
def winnerOfGame(game_board, tile):
# check horizontal spaces
for y in range(height):
for x in range(width - 3):
if game_board[x][y] == tile and game_board[x+1][y] == tile and game_board[x+2][y] == tile and game_board[x+3][y] == tile:
return True
# check vertical spaces
for x in range(width):
for y in range(height - 3):
if game_board[x][y] == tile and game_board[x][y+1] == tile and game_board[x][y+2] == tile and game_board[x][y+3] == tile:
return True
# check / diagonal spaces
for x in range(width - 3):
for y in range(3, height):
if game_board[x][y] == tile and game_board[x+1][y-1] == tile and game_board[x+2][y-2] == tile and game_board[x+3][y-3] == tile:
return True
# check \ diagonal spaces
for x in range(width - 3):
for y in range(height - 3):
if game_board[x][y] == tile and game_board[x+1][y+1] == tile and game_board[x+2][y+2] == tile and game_board[x+3][y+3] == tile:
return True
return False
def playAgain():
# This function returns True if the player wants to play again, otherwise it returns False.
print('Do you want to play again? (yes or no)')
return input().lower().startswith('y')
def main():
print('Four In A Row')
print()
while True:
turn = whoGoesFirst()
print('The %s player will go first.' % (turn))
gameBoard = getNewBoard()
while True:
if turn == 'Player 1':
drawBoard(gameBoard)
move1 = getPlayer1Move(gameBoard)
p1makeMove(gameBoard, player1Marker, move1)
if winnerOfGame(gameBoard, player1Marker):
winner = 'Player 1'
break
turn = 'Player 2'
elif turn == 'Player 2':
drawBoard(gameBoard)
move2 = getPlayer2Move(gameBoard)
p2makeMove(gameBoard, player2Marker, move2)
if winnerOfGame(gameBoard, player2Marker):
winner = 'Player 2'
break
turn = 'Player 1'
elif isBoardFull(game_board):
winner = 'tie'
break
drawBoard(mainBoard)
print('Winner is: %s' % winner)
if not playAgain():
break
main()
This is is the error that is displayed after the game board:
Traceback (most recent call last):
File "/Users/EshaS/Documents/Connect4Final.py", line 166, in <module>
main()
File "/Users/EshaS/Documents/Connect4Final.py", line 151, in main
move2 = getPlayer2Move(gameBoard)
File "/Users/EshaS/Documents/Connect4Final.py", line 53, in getPlayer2Move
print('Player 2: Which column do you want to move on (1-7)? Type quit if you want to quit: ' % (width))
TypeError: not all arguments converted during string formatting
At the end of your print, what are you trying to do with % (width)?
You can use:
print('Player 2: [...] %d [...]' % (width))
%d says that you will include width as an int to a specific location in the string you are printing.
e.g.:
print('Player 2: Which column do you want to move on (1-%d)? Type quit if you want to quit:' % (width))
Which will print (assuming that width = 7): Player 2: Which column do you want to move on (1-7)? Type quit if you want to quit:.
By the way, (code just after that), it is better to parse your input. To use the lower function, you need your input to be a string. And .int() function is unclear, best is to use isinstance( <var>, int ). Which gives:
move = str(input())
if move.lower().startswith('q'):
sys.exit()
if isinstance( move, int ):
continue
(This is True for both functions: getPlayer1Move and getPlayer2Move).
Related
I'm new to python and writing my first project. I'm trying to implement a check that if a space is already occupied, not to move there. I can't seem to figure out why my move_player method overwrites the index value of the board even though I am explicitly checking it (If it is O's turn and X has already been placed in the index O is trying to move to, it just overwrites it). I have tried hard coding the check for 'X' and 'O' instead of player.player as well but can't seem to figure it out. Does it have to do something with how Python works or am I implementing it wrong?
class Player:
def __init__(self, player):
self.player = player
class Board:
def __init__(self):
self.board = [[' ' for i in range(3)] for j in range(3)]
def display_board(self):
print('---------')
for row in self.board:
print('| ', end='')
for col in row:
print(f'{col} ', end='')
print('|')
print('---------')
def move_player(self, player):
try:
p1 = Player('X')
p2 = Player('O')
coordinates = [int(i) for i in input("Enter coordinates for move: ").split()]
xCoordinate = coordinates[0]
yCoordinate = coordinates[1]
if ((self.board[xCoordinate][yCoordinate] == p1.player) or
(self.board[xCoordinate][yCoordinate] == p2.player)):
print("That space is occupied, please choose another one.")
self.move_player(player)
else:
self.board[xCoordinate - 1][yCoordinate - 1] = player.player
except (ValueError, IndexError):
print("Please only enter numbers between 1 and 3.")
self.move_player(player)
def has_won(self, player):
if self.check_diagonal(player):
return True
elif self.check_across(player):
return True
elif self.check_down(player):
return True
return False
if __name__ == '__main__':
board = Board()
player1 = Player('X')
player2 = Player('O')
player = player1
while True:
board.display_board()
board.move_player(player)
if board.has_won(player):
board.display_board()
print(f'{player.player} wins!!!')
break
if player == player1:
player = player2
else:
player = player1
The code is very convoluted but from what I can see:
if ((self.board[xCoordinate][yCoordinate] == p1.player) or
(self.board[xCoordinate][yCoordinate] == p2.player)):
...
self.board[xCoordinate - 1][yCoordinate - 1] = player.player
You are checking [x,y] but assigning to [x-1,y-1].
I'm trying to make a simple console tic tac toe in Python.
What I want is to let the player to choose the position is going to do the next play by moving the cursor with arrow keys
By investigating a bit I've already been able to replace actual strings in a board's position with str.format() and changing the string that position has with an array by an user input.
The following code shows how I achived that.
It only replaces the first position as just an example .
from os import system
board = ["a",'b','c','c','d','e','f','g','h']
#Draws the board
def drawboard():
print(" {0[0]} | {0[1]} | {0[2]} ".format(board))
print("___|___|___")
print(" {0[3]} | {0[4]} | {0[5]} ".format(board))
print("___|___|___")
print(" {0[6]} | {0[7]} | {0[8]} ".format(board))
print(" | | ")
drawboard()
userInput = input()
board[0] = userInput
system("cls") #clears the screen
drawboard()
Writing Tic Tac Toe in console can be done using curses. Its API is pretty straight forward for working with display coordinates. This script will work as described by you above, allowing to navigate with the arrow keys and putting on a key stroke the respective X or O signs. I works well in my shell
import curses
CH_P1 = 'X'
CH_P2 = 'O'
X_STEP = 4
Y_STEP = 2
X_OFFSET = 1
Y_OFFSET = 4
def print_board(stdscr):
stdscr.addstr(0, 0, 'Tic Tac Toe')
stdscr.hline(1, 0, '-', 50)
stdscr.addstr(2, 0, 'Use arrows to move, [SPACE] Draw, [Q] Quit')
stdscr.addstr(Y_OFFSET , X_OFFSET, ' │ │ ')
stdscr.addstr(Y_OFFSET + 1, X_OFFSET, '──┼───┼──')
stdscr.addstr(Y_OFFSET + 2, X_OFFSET, ' │ │ ')
stdscr.addstr(Y_OFFSET + 3, X_OFFSET, '──┼───┼──')
stdscr.addstr(Y_OFFSET + 4, X_OFFSET, ' │ │ ')
def print_players(stdscr, player_id):
stdscr.addstr(Y_OFFSET + 6, 0, 'Player {}'.format(CH_P1),
curses.A_BOLD if player_id == 0 else 0)
stdscr.addstr(Y_OFFSET + 7, 0, 'Player {}'.format(CH_P2),
curses.A_BOLD if player_id == 1 else 0)
def draw(y, x, stdscr, player_id):
stdscr.addch(y, x, CH_P2 if player_id else CH_P1)
def check_victory(board, y, x):
#check if previous move caused a win on horizontal line
if board[0][x] == board[1][x] == board [2][x]:
return True
#check if previous move caused a win on vertical line
if board[y][0] == board[y][1] == board [y][2]:
return True
#check if previous move was on the main diagonal and caused a win
if x == y and board[0][0] == board[1][1] == board [2][2]:
return True
#check if previous move was on the secondary diagonal and caused a win
if x + y == 2 and board[0][2] == board[1][1] == board [2][0]:
return True
return False
def main(stdscr):
# Clear screen
# stdscr.clear()
print_board(stdscr)
player_id = 0
print_players(stdscr, player_id=player_id)
x_pos = 1
y_pos = 1
board = [list(' ') for _ in range(3)]
# This raises ZeroDivisionError when i == 10.
while True:
stdscr.move(Y_OFFSET + y_pos * Y_STEP, X_OFFSET + x_pos * X_STEP)
c = stdscr.getch()
if c == curses.KEY_UP:
y_pos = max(0, y_pos - 1)
elif c == curses.KEY_DOWN:
y_pos = min(2, y_pos + 1)
elif c == curses.KEY_LEFT:
x_pos = max(0, x_pos - 1)
elif c == curses.KEY_RIGHT:
x_pos = min(2, x_pos + 1)
elif c == ord('q') or c == ord('Q'):
break
elif c == ord(' '):
# Update
y, x = stdscr.getyx()
if stdscr.inch(y, x) != ord(' '):
continue
draw(y, x, stdscr, player_id)
board[y_pos][x_pos] = CH_P2 if player_id else CH_P1
if check_victory(board, y_pos, x_pos):
stdscr.addstr(Y_OFFSET + 9, 0, 'Player {} wins'.format(
CH_P2 if player_id else CH_P1))
break
# Switch player
player_id = (player_id + 1) % 2
print_players(stdscr, player_id)
stdscr.refresh()
stdscr.getkey()
if __name__ == '__main__':
curses.wrapper(main)
I have an excercise to do and I'm stuck. It's the board game Alak, not much known, that I have to code in python. I can link the execrcise with the rules so you can help me better. The code has the main part and the library with all the procedures and function.
from Library_alak import *
n = 0
while n < 1:
n = int(input('Saisir nombre de case strictement positif : '))
loop = True
player = 1
player2 = 2
removed = [-1]
board = newboard(n)
display(board, n)
while loop:
i = select(board, n, player, removed)
print(i)
board = put(board, player, i)
display(board, n)
capture(board, n, player, player2)
loop = True if again(board, n, player, removed) is True else False
if player == 1 and loop:
player, player2 = 2, 1
elif player == 2 and loop:
player, player2 = 1, 2
win(board, n)
print(win(board, n))
And here is the library:
def newboard(n):
board = ([0] * n)
return board
def display(board, n):
for i in range(n):
if board[i] == 1:
print('X', end=' ')
elif board[i] == 2:
print('O', end=' ')
else:
print(' . ', end=' ')
def capture(board, n, player, player2):
for place in range(n):
if place == player:
place_beginning = place
while board[place] != player:
place_end = place
if board[place + x] == player:
return board
else:
return board
def again(board, n, player, removed):
for p in board(0):
if p == 0:
if p not in removed:
return True
else:
return False
def possible(n, removed, player, i, board):
for p in range(n + 1):
if p == 1:
if board[p-1] == 0:
if p not in removed:
return True
else:
return False
def win(board, n):
piecesp1 = 0
piecesp2 = 0
for i in board(0):
if i == 1:
piecesp1 += 1
else:
piecesp2 += 1
if piecesp1 > piecesp2:
print('Victory : Player 1')
elif piecesp2 > piecesp1:
print('Victory : Player 2')
else:
return 'Equality'
def select(board, n, player, removed):
loop = True
while loop:
print('player', player)
i = int(input('Enter number of boxes : '))
loop = False if possible(n, removed, player, i, board)is True else True
return i
def put(board, player, i):
i -= 1
if board[i] == 0:
if player == 1:
board[i] = 1
return board
else:
board[i] = 2
return board
else:
put(board, player, i)
So my problems here are that I have few errors, the first one is that when I enter the number '1' when asked to enter a number of boxes ( which is the place to play on ) nothing happens. Then when entering any other number, either the error is : if board[place + x] == player:
NameError: name 'x' is not defined
or there seems to be a problem with the : if board[place + x] == player:
NameError: name 'x' is not defined
I would appreciate a lot if anyone could help me. I'm conscious that it might not be as detailed as it should be and that you maybe don't get it all but you can contact me for more.
Rules of the Alak game:
Black and white take turns placing stones on the line. Unlike Go, this placement is compulsory if a move is available; if no move is possible, the game is over.
No stone may be placed in a location occupied by another stone, or in a location where a stone of your own colour has just been removed. The latter condition keeps the game from entering a neverending loop of stone placement and capture, known in Go as ko.
If placing a stone causes one or two groups of enemy stones to no longer have any adjacent empty spaces--liberties, as in Go--then those stones are removed. As the above rule states, the opponent may not play in those locations on their following turn.
If placing a stone causes one or two groups of your own colour to no longer have any liberties, the stones are not suicided, but instead are safe and not removed from play.
You shouldn't use "player2" as a variable, there's an easier way, just use "player" which take the value 1 or 2 according to the player. You know, something like that : player = 1 if x%2==0 else 2
and x is just a increasing int from 0 until the end of the game.
I am trying to write a program for connect 4 but am having a lot of trouble getting past the directions. Everything under the comment, "#everything works up to here" works but then it all explodes and I have no idea even where to start to fix it.
#connect 4
import random
#define global variables
X = "X"
O = "O"
EMPTY = "_"
TIE = "TIE"
NUM_ROWS = 6
NUM_COLS = 8
def display_instruct():
"""Display game instructions."""
print(
"""
Welcome to the second greatest intellectual challenge of all time: Connect4.
This will be a showdown between your human brain and my silicon processor.
You will make your move known by entering a column number, 1 - 7. Your move
(if that column isn't already filled) will move to the lowest available position.
Prepare yourself, human. May the Schwartz be with you! \n
"""
)
def ask_yes_no(question):
"""Ask a yes or no question."""
response = None
while response not in ("y", "n"):
response = input(question).lower()
return response
def ask_number(question,low,high):
"""Ask for a number within range."""
#using range in Python sense-i.e., to ask for
#a number between 1 and 7, call ask_number with low=1, high=8
low=1
high=NUM_COLS
response = None
while response not in range (low,high):
response=int(input(question))
return response
def pieces():
"""Determine if player or computer goes first."""
go_first = ask_yes_no("Do you require the first move? (y/n): ")
if go_first == "y":
print("\nThen take the first move. You will need it.")
human = X
computer = O
else:
print("\nYour bravery will be your undoing... I will go first.")
computer = X
human = O
return computer, human
def new_board():
board = []
for x in range (NUM_COLS):
board.append([" "]*NUM_ROWS)
return board
def display_board(board):
"""Display game board on screen."""
for r in range(NUM_ROWS):
print_row(board,r)
print("\n")
def print_row(board, num):
"""Print specified row from current board"""
this_row = board[num]
print("\n\t| ", this_row[num], "|", this_row[num], "|", this_row[num], "|", this_row[num], "|", this_row[num], "|", this_row[num], "|", this_row[num],"|")
print("\t", "|---|---|---|---|---|---|---|")
# everything works up to here!
def legal_moves(board):
"""Create list of column numbers where a player can drop piece"""
legal=True
while not legal:
col = input("What column would you like to move into (1-7)?")
for row in range (6,0,1):
if (1 <= row <= 6) and (1 <= col <= 7) and (board[row][col]==" "):
board[row][col] = turn
legal = True
else:
print("Sorry, that is not a legal move.")
def human_move(board,human):
"""Get human move"""
try:
legals = legal_moves(board)
move = None
while move not in legals:
move = ask_number("Which column will you move to? (1-7):", 1, NUM_COLS)
if move not in legals:
print("\nThat column is already full, nerdling. Choose another.\n")
print("Human moving to column", move)
return move #return the column number chosen by user
except NameError:
print ("Only numbers are allowed.")
except IndexError:
print ("You can only select colums from 1-7.")
def get_move_row(turn,move):
for m in (NUM_COLS):
place_piece(turn,move)
display_board()
def computer_move ():
move= random.choice(legal)
return move
def place_piece(turn,move):
if this_row[m[move]]==" ":
this_row.append[m[move]]=turn
def winner(board):
# Check rows for winner
for row in range(6):
for col in range(3):
if (board[row][col] == board[row][col + 1] == board[row][col + 2] == board[row][col + 3]) and (board[row][col] != " "):
return [row][col]
# Check columns for winner
for col in range(6):
for row in range(3):
if (board[row][col] == board[row + 1][col] == board[row + 2][col] ==board[row + 3][col]) and (board[row][col] != " "):
return [row][col]
# Check diagonal (top-left to bottom-right) for winner
for row in range(3):
for col in range (4):
if (board[row][col] == board[row + 1][col + 1] == board[row + 2][col + 2] == board[row + 3][col + 3]) and (board[row][col] != " "):
return true
# Check diagonal (bottom-left to top-right) for winner
for row in range (5,2,-1):
for col in range (3):
if (board[row][col] == board[row - 1][col + 1] == board[row - 2][col + 2] == board[row - 3][col + 3]) and (board[row][col] != " "):
return [row][col]
# No winner
return False
def main():
display_instruct()
computer,human = pieces()
turn = X
board = new_board()
while not winner(board) and (" " not in board):
display_board(board)
if turn == human:
human_move(board,human)
get_move_row()
place_piece()
else:
computer_move(board,computer)
place_piece()
display_board(board)
turn = next_turn()
the_winner = winner(board)
congrat_winner(the_winner, computer, human)
#start the program
main ()
input ("\nPress the enter key to quit.")
For fun, here's an object-oriented refactorization. It's a bit long, but well documented and should be easy to understand.
I started with your code and split it into Board, Player, and Game classes, then derived Computer and Human classes from Player.
Board knows the shape and size of the rack, what moves are legal, and recognizes when wins and ties occur
Player has a name and knows how to choose (or prompt for) a legal move
Game has a Board and two Players and controls turn-taking and output
I'm not 100% happy with it - Board has a .board that is a list of list of string, but Game has a .board that is a Board; a bit of judicious renaming would be a good idea - but for an hour's work it's pretty solid.
Hope you find this educational:
# Connect-4
from itertools import cycle, groupby
from random import choice
from textwrap import dedent
import sys
# version compatibility shims
if sys.hexversion < 0x3000000:
# Python 2.x
inp = raw_input
rng = xrange
else:
# Python 3.x
inp = input
rng = range
def get_yn(prompt, default=None, truthy={"y", "yes"}, falsy={"n", "no"}):
"""
Prompt for yes-or-no input
Return default if answer is blank and default is set
Return True if answer is in truthy
Return False if answer is in falsy
"""
while True:
yn = inp(prompt).strip().lower()
if not yn and default is not None:
return default
elif yn in truthy:
return True
elif yn in falsy:
return False
def get_int(prompt, lo=None, hi=None):
"""
Prompt for integer input
If lo is set, result must be >= lo
If hi is set, result must be <= hi
"""
while True:
try:
value = int(inp(prompt))
if (lo is None or lo <= value) and (hi is None or value <= hi):
return value
except ValueError:
pass
def four_in_a_row(tokens):
"""
If there are four identical tokens in a row, return True
"""
for val,iterable in groupby(tokens):
if sum(1 for i in iterable) >= 4:
return True
return False
class Board:
class BoardWon (BaseException): pass
class BoardTied(BaseException): pass
EMPTY = " . "
HOR = "---"
P1 = " X "
P2 = " O "
VER = "|"
def __init__(self, width=8, height=6):
self.width = width
self.height = height
self.board = [[Board.EMPTY] * width for h in rng(height)]
self.tokens = cycle([Board.P1, Board.P2])
self.rowfmt = Board.VER + Board.VER.join("{}" for col in rng(width)) + Board.VER
self.rule = Board.VER + Board.VER.join(Board.HOR for col in rng(width)) + Board.VER
def __str__(self):
lines = []
for row in self.board:
lines.append(self.rowfmt.format(*row))
lines.append(self.rule)
lines.append(self.rowfmt.format(*("{:^3d}".format(i) for i in rng(1, self.width+1))))
lines.append("")
return "\n".join(lines)
def is_board_full(self):
return not any(cell == Board.EMPTY for cell in self.board[0])
def is_win_through(self, row, col):
"""
Check for any winning sequences which pass through self.board[row][col]
(This is called every time a move is made;
thus any win must involve the last move,
and it is faster to check just a few cells
instead of the entire board each time)
"""
# check vertical
down = min(3, row)
up = min(3, self.height - row - 1)
tokens = [self.board[r][col] for r in rng(row - down, row + up + 1)]
if four_in_a_row(tokens):
return True
# check horizontal
left = min(3, col)
right = min(3, self.width - col - 1)
tokens = [self.board[row][c] for c in rng(col - left, col + right + 1)]
if four_in_a_row(tokens):
return True
# check upward diagonal
down = left = min(3, row, col)
up = right = min(3, self.height - row - 1, self.width - col - 1)
tokens = [self.board[r][c] for r,c in zip(rng(row - down, row + up + 1), rng(col - left, col + right + 1))]
if four_in_a_row(tokens):
return True
# check downward diagonal
down = right = min(3, row, self.width - col - 1)
up = left = min(3, self.height - row - 1, col)
tokens = [self.board[r][c] for r,c in zip(rng(row - down, row + up + 1), rng(col + right, col - left - 1, -1))]
if four_in_a_row(tokens):
return True
# none of the above
return False
def legal_moves(self):
"""
Return a list of columns which are not full
"""
return [col for col,val in enumerate(self.board[0], 1) if val == Board.EMPTY]
def do_move(self, column):
token = next(self.tokens)
col = column - 1
# column is full?
if self.board[0][col] != Board.EMPTY:
next(self.move) # reset player token
raise ValueError
# find lowest empty cell (guaranteed to find one)
for row in rng(self.height-1, -1, -1): # go from bottom to top
if self.board[row][col] == Board.EMPTY: # find first empty cell
# take cell
self.board[row][col] = token
# did that result in a win?
if self.is_win_through(row, col):
raise Board.BoardWon
# if not, did it result in a full board?
if self.is_board_full():
raise Board.BoardTied
# done
break
class Player:
def __init__(self, name):
self.name = name
def get_move(self, board):
"""
Given the current board state, return the row to which you want to add a token
"""
# you should derive from this class instead of using it directly
raise NotImplemented
class Computer(Player):
def get_move(self, board):
return choice(board.legal_moves())
class Human(Player):
def get_move(self, board):
legal_moves = board.legal_moves()
while True:
move = get_int("Which column? (1-{}) ".format(board.width), lo=1, hi=board.width)
if move in legal_moves:
return move
else:
print("Please pick a column that is not already full!")
class Game:
welcome = dedent("""
Welcome to the second greatest intellectual challenge of all time: Connect4.
This will be a showdown between your human brain and my silicon processor.
You will make your move known by entering a column number, 1 - 7. Your move
(if that column isn't already filled) will move to the lowest available position.
Prepare yourself, human. May the Schwartz be with you!
""")
def __init__(self):
print(Game.welcome)
# set up new board
self.board = Board()
# set up players
self.players = cycle([Human("Dave"), Computer("HAL")])
# who moves first?
if get_yn("Do you want the first move? (Y/n) ", True):
print("You will need it...\n")
# default order is correct
else:
print("Your rashness will be your downfall...\n")
next(self.players)
def play(self):
for player in self.players:
print(self.board)
while True:
col = player.get_move(self.board) # get desired column
try:
print("{} picked Column {}".format(player.name, col))
self.board.do_move(col) # make the move
break
except ValueError:
print("Bad column choice - you can't move there")
# try again
except Board.BoardWon:
print("{} won the game!".format(player.name))
return
except Board.BoardTied:
print("The game ended in a stalemate")
return
def main():
while True:
Game().play()
if not get_yn("Do you want to play again? (Y/n) ", True):
break
if __name__=="__main__":
main()
I'm writing a tic-tac-toe game for an assignment. It needs to use object-oriented programming and it has to be relatively smart - it needs to block the player's success. I'm having a lot of trouble with this.
My trouble comes from my rowabouttowin method: I did a very convoluted list comprehension and I don't think that I did it correctly.
What I want is a method that checks if the player is about to win the game horizontally (O _ O, O O _, or _ O O if mark = O) and, if the player is, returns the position where the computer should play.
Any help or advice on how best to approach this?
from random import randint
class TTT:
board = [[' ' for row in range(3)] for col in range(3)]
currentgame = []
def print(self):
"""Displays the current board."""
print("\n-----\n".join("|".join(row) for row in self.board))
def mark(self,pos,mark):
"""Method that places designated mark at designated position on the board."""
x,y = pos
self.board[x][y] = mark
def win(self,mark):
"""Method that checks if someone has won the game."""
if mark == self.board[0][0] == self.board[1][1] == self.board[2][2]:
return True
if mark == self.board[2][0] == self.board[1][1] == self.board[0][2]:
return True
elif mark == self.board[0][0] == self.board[1][0] == self.board[2][0]:
return True
elif mark == self.board[1][0] == self.board[1][1] == self.board[1][2]:
return True
elif mark == self.board[0][1] == self.board[1][1] == self.board[2][1]:
return True
elif mark == self.board[0][2] == self.board[1][2] == self.board[2][2]:
return True
elif mark == self.board[0][0] == self.board[0][1] == self.board[0][2]:
return True
elif mark == self.board[2][0] == self.board[2][1] == self.board[2][2]:
return True
else:
return False
def choose(self,mark):
"""The computer chooses a place to play. If the player is not about to win,
plays randomly. Otherwise, does a series of checks to see if the player is about
to win horizontally, vertically, or diagonally. I only have horizontal done."""
spotx = randint(0,2)
spoty = randint(0,2)
if self.rowabouttowin(mark):
self.mark((self.rowabouttowin(mark)),mark)
elif self.legalspace(spotx,spoty):
self.mark((spotx,spoty),mark)
else:
self.choose(mark)
def legalspace(self,spotx,spoty):
"""Returns True if the provided spot is empty."""
if self.board[spotx][spoty] == ' ':
return True
else:
return False
def rowabouttowin(self,mark):
"""If the player is about to win via a horizontal 3-in-a-row,
returns location where the computer should play"""
for row in range(3):
if any(' ' == self.board[row][1] for i in range(3)) and any(self.board[row][i] == self.board[row][j] for i in range(3) for j in range(3)):
if self.board[row][i] == ' ' : yield(self.board[row][i % 3], self.board[row][i])
This currently gives this error message:
Traceback (most recent call last):
File "<pyshell#49>", line 1, in <module>
x.choose('x')
File "/Users/richiehoffman/Documents/Python Programs/Tic Tac Toe.py", line 40, in choose
self.mark((self.rowabouttowin(mark)),mark)
File "/Users/richiehoffman/Documents/Python Programs/Tic Tac Toe.py", line 11, in mark
x,y = pos
File "/Users/richiehoffman/Documents/Python Programs/Tic Tac Toe.py", line 61, in rowabouttowin
if self.board[row][i] == ' ' : yield(self.board[row][i % 3], self.board[row][i])
NameError: global name 'i' is not defined
A few tips:
You're using class variables, not instance variables, so look up the difference. I changed your class to use instance variables, as the variables that you set should belong in an instance.
Consider making things more readable.
Use __str__ to make the printable version of your class. That way, you can do print(class_instance) and it'll come out all nice.
Here's what I changed:
from random import randint
class TTT(object):
def __init__(self):
self.board = [[' ' for row in range(3)] for col in range(3)]
self.currentgame = []
def __str__(self):
"""Displays the current board."""
return "\n-----\n".join("|".join(row) for row in self.board)
def mark(self, pos, mark):
"""Method that places designated mark at designated position on the board."""
x, y = pos
self.board[x][y] = mark
def win(self, mark):
"""Method that checks if someone has won the game."""
for row in self.board:
if row[0] == row[1] == row[2]:
return True
for i in range(3):
if self.board[0][i] == self.board[1][i] == self.board[2][i]:
return True
if board[0][0] == board[1][1] == board[2][2]:
return True
elif board[0][2] == board[1][1] == board[2][0]:
return True
else:
return False
def choose(self, mark):
"""The computer chooses a place to play. If the player is not about to win,
plays randomly. Otherwise, does a series of checks to see if the player is about
to win horizontally, vertically, or diagonally. I only have horizontal done."""
spotx = randint(0, 2)
spoty = randint(0, 2)
if self.rowabouttowin(mark):
self.mark((self.rowabouttowin(mark)), mark)
elif self.legalspace(spotx, spoty):
self.mark((spotx, spoty), mark)
else:
self.choose(mark)
def legalspace(self, spotx, spoty):
"""Returns True if the provided spot is empty."""
return self.board[spotx][spoty] == ' '
def rowabouttowin(self, mark):
"""If the player is about to win via a horizontal 3-in-a-row,
returns location where the computer should play"""
for row in range(3):
check_one = any(' ' == self.board[row][1] for i in range(3))
check_two = any(self.board[row][i] == self.board[row][j] for i in range(3) for j in range(3))
# I have no idea what this code does
if self.board[row][i] == ' ' :
yield self.board[row][i % 3], self.board[row][i]