Pieces doesn't stack after one loop on my connect4 - python

I'm working on a connect4 and I have an issue on the piece slide down part.
When I launch the game, the pieces stacked themself only once like that.
000000
000000
000000
000000
000000
PP0000
XX0000
But after that, it doesn't stack anymore. If the user chose to put a piece on the first or second row it won't stack the piece over the actual one. I tried to figure out where the issue was coming from but I don't see it and beeing new in python doesn't help a lot.
board = []
for x in range(0, 7):
board.append(["O"] * 6)
def print_board(board):
for row in board:
print(" ".join(row))
def user_one(board):
status = 0
clm = 0
first_x = int(input("Enter a Row: "))
for i in board:
while i == "0":
clm += 1
if board[clm - 1][first_x - 1] == "P":
board[clm - 2][first_x - 1] = "X"
else:
board[clm - 1][first_x - 1] = "X"
status = 1
print_board(board)
def user_two(board):
status = 0
clm = 0
second_p = int(input("Enter a Row: "))
for i in board:
while i == "0":
clm += 1
if board[clm - 1][second_p - 1] == "X":
board[clm - 2][second_p - 1] = "P"
else:
board[clm - 1][second_p - 1] = "P"
status = 2
print_board(board)
def launch_game(board):
while True:
user_one(board)
user_two(board)
launch_game(board)

I think overall was a bit of confusion between column and row. I took your code and modified it a little bit. So this should work:
board = []
for x in range(0, 7):
board.append(["O"] * 6)
def print_board(board):
for row in board:
print(" ".join(row))
def user_one(board):
clm = 0
selected_column = int(input("Enter a column: "))
for i in reversed(range(len(board))):
if board[i][selected_column-1] == "O":
board[i][selected_column-1] = "X"
break
print_board(board)
def user_two(board):
clm = 0
selected_column = int(input("Enter a column: "))
for i in reversed(range(len(board))):
if board[i][selected_column-1] == "O":
board[i][selected_column-1] = "P"
break
print_board(board)
def launch_game(board):
while True:
user_one(board)
user_two(board)
launch_game(board)
You can still improve on removing unused variables, names and refactoring the functions (so that you only have one for your piece slide down).

Related

N-Queens program in Python

The N Queen is the problem of placing N chess queens on an N×N chessboard so that no two queens attack each other. I have solved this program earlier, but am trying to rework my code to mirror that which I used to fashion a sudoku solver. I cannot seem to find the logical error but when I run the code, nothing prints. My program is attached below and if anyone could find my error that would be very helpful!
import numpy as np
def main():
global n
n = input("Enter N")
n = int(n)
global board
board = np.zeros((n,n), dtype=int)
solve_board()
def solve_board():
for row in range(n):
for col in range(n):
if board[row][col] == 0: #no queen
if (is_valid (board,row,col,n)):
board[row][col] = 1 #Assigning 1 for queen
solve_board()
board[row][col] = 0
return False
print('-'*n)
for row in board:
for col in row:
if col == 1:
print ("Q", end = " ")
else:
print (".", end = " ")
def is_valid(board,i,j,n):
if 1 in board[i]: #Checking row
return False
for row in range(0,i): #Checking column
if (board[row][j]==1):
return False
x,y = i,j
while (x>=0 and y>=0): #left diagonal
if (board[x][y]==1):
return False
x-=1
y-=1
x,y = i,j
while (x>=0 and y<n): #right diagonal
if (board[x][y]==1):
return False
x-=1
y+=1
return True
if __name__ == "__main__":
main()
This is how I had solved this code earlier, with solve_board being altered as followed.
def solve_board(row):
if(row == n):
print('-'*n)
for row in board:
for col in row:
if col == 1:
print ("Q", end = " ")
else:
print (".", end = " ")
print("")
else:
for col in range(n):
if (is_valid (board,row,col,n)):
board[row][col]=1
solve_board(row+1)
board[row][col] = 0
return False
Here is where the inspiration for my current code came from, a sudoku solver that I designed where I used 2 nested for loops; one for rows and one for columns. Based on this I had altered my solve_board(row) in my original n-queens code to the current function without a parameter. This sudoku code works perfectly.
def solve_board():
global board
for rowno in range(9):
#print ("row",rowno)
for colno in range(9):
#print("col",colno)
if board[rowno][colno] == 0:
for i in range(1,10):
#print(i)
if (is_valid(board,rowno,colno,i)):
board[rowno][colno]=i
solve_board()
board[rowno][colno]=0
return False
print (np.matrix(board))
I think the issue might lie in the fact that in the N-Queens problem the board does not fill up, i.e there are still 0s while for sudoku the entire board fills up and therefore when the ''if board[row][col] == 0'' is proven false exits the loop and prints. In the N-Queens problem since zero's are always present, it becomes an issue.
Try this
import numpy as np
def main():
global n
n = input("Enter N: ")
n = int(n)
global board
board = np.zeros((n,n), dtype=int)
solve_board()
def print_board():
print('-'*n)
for row in board:
for col in row:
if col == 1:
print ("Q", end = " ")
else:
print (".", end = " ")
print()
def is_valid(board,i,j,n):
if 1 in board[i]: #Checking row
return False
for row in range(0, n): #Checking column
if (board[row][j]==1):
return False
for k in range(0,n):
for l in range(0,n):
if (k+l==i+j) or (k-l==i-j):
if board[k][l]==1:
return False
return True
def solve_board():
for row in range(n):
for col in range(n):
if board[row][col] == 0: #no queen
if (is_valid (board,row,col,n)):
board[row][col] = 1 #Assigning 1 for queen
if np.count_nonzero(board) == n:
print_board()
return True
solve_board()
board[row][col] = 0
else:
return False
if __name__ == "__main__":
main()

I'm stuck in the creating of a Board game in python

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.

PYTHON: Noughts and Crosses

I have created a multiplayer game of Noughts and Crosses using Python 3.
I have got all of my code to work except for the check loop.
The check loop will each time a new symbol is entered if it is a winning move or if the board is full.
Currently, my code does not finish after one player inputs 3 symbols in a row or the board becomes full.
This is my code so far:
import random
def start_player():
startplayer = random.randint(1,2)
if startplayer == 1:
turn = 'X'
print("Player One (X) will start the game.")
else:
startplayer == 2
turn = 'O'
print("Player Two (O) will start the game.")
return turn
def getmove():
row = int(input("Please enter a number between 0 and 2: "))
column = int(input("Please enter a number between 0 and 2: "))
while grid[row][column] != "":
print("Invalid move.")
row = int(input("Please enter a number between 0 and 2: "))
column = int(input("Please enter a number between 0 and 2: "))
return row, column
def mainturn(row, column):
global countmove
countmove = countmove + 1
global symbol
grid[row][column] = symbol
for y in range(0,(len(grid))):
print(grid[y])
if symbol == 'X':
symbol = 'O'
elif symbol == 'O':
symbol = 'X'
return countmove
def check_win(row, column, symbol):
if (grid[0][0] and grid[0][1] and grid[0][2] == symbol) or (grid[1][0] and grid[1][1] and grid[1][2] == symbol) or (grid[2][0] and grid[2][1] and grid[2][2] == symbol) or (grid[0][0] and grid[1][0] and grid[2][0] == symbol) or (grid[0][1] and grid[1][1] and grid[2][1] == symbol)or (grid[0][2] and grid[1][2] and grid[2][2] == symbol)or (grid[0][0] and grid[1][1] and grid[2][2] == symbol) or (grid[2][0] and grid[1][1] and grid[0][2] == symbol):
print("Well done!",symbol," won the game.")
return true
elif countmove == 9:
print("Board Full. Game over.")
#main program
grid = [["","",""],["","",""],["","",""]]
countmove = 0
win = 'false'
for y in range(0,(len(grid))):
print(grid[y])
symbol = start_player()
while countmove != 9 or win == 'false':
countmove = 0
row, column = getmove()
mainturn(row,column)
win = check_win(row,column, symbol)
It is because of two things. Firstly you are resetting count_move value in the loop, hence it is always zero and goes in an infinite loop. Secondly you are changing the symbol before checking if it is a win, check_win routine was checking for the next symbol not the current symbol of the player. Here is the working code:
while countmove != 9 or win == 'false':
row, column = getmove()
mainturn(row,column)
win = check_win(row,column, symbol)
symbol = change_symbol(symbol)
The sub-routine change_symbol is here:
def change_symbol(symbol):
if symbol == 'X':
symbol = 'O'
elif symbol == 'O':
symbol = 'X'
return symbol
As part of good coding practice, avoid using global variables inside subroutines, it will just add up the confusion.
I did my best to comment on why I did things and why I removed things. I hope this example helps you see why it cant be better to use classes when working with python. I added some other features that I think may help you as you continue to learn programming
import random
# Lets put all these functions into a class
class Game:
# Lets set up the game
def __init__(self, player_one="X", player_two="O"):
self.player_one = player_one
self.player_two = player_two
# Where the game board is stored
# Using periods instead of spaces so the players can see the possible moves
self.game_board = [
[".", ".", "."],
[".", ".", "."],
[".", ".", "."]
]
# This value is to check if the game is still going
self.running = True
# Whos turn is it?
self.active_player = ""
# The tasks we HAVE to do to make the game work
self.start_player()
self.run_game()
# The function is part of the Game class so we have to pass self into it.
def start_player(self):
# Randomly Choose a starting player
startplayer = random.randint(1,2)
if startplayer == 1:
# We declared the string values in the __init__ function
player = self.player_one
print("Player One ({}) will start the game.".format(player))
else:
startplayer == 2
player = self.player_two
print("Player Two ({}) will start the game.".format(player))
# Set the initial player
self.active_player = player
def get_move(self):
# Seems silly to have them enter the rows and columns one by one
#row = int(input("Please enter a number between 0 and 2: "))
#column = int(input("Please enter a number between 0 and 2: "))
# Show the player whos turn it is
input_data = input("Player ({}) please choose a Column and a Row: ".format(self.active_player))
# Default values that aren't in the game, if they arent changed they will be caught
row = -1
column = -1
# Users entry all types of funky data, lets make sure its right
try:
r, c = input_data.split(" ")
r = int(r)
c = int(c)
if r >= 0 and r <= 3:
row = int(r)
if c >= 0 and c <= 3:
column = int(c)
except:
print("Enter only two numbers (0, 1, or 2) seperated by a space")
return row, column
# This check for the grid should be its own function
#while grid[row][column] != "":
# print("Invalid move.")
# row = int(input("Please enter a number between 0 and 2: "))
# column = int(input("Please enter a number between 0 and 2: "))
def check_move(self, row, column):
if row == -1 or column == -1:
return False
# If the space is blank return True
if self.game_board[row][column] == ".":
return True
print("{} {} is an invalid move, try again".format(row, column))
return False
# Add another function to print out the board for us
def show_board(self):
for row in self.game_board:
row_string = ""
for cell in row:
row_string = "{} {} ".format(row_string, cell)
print(row_string)
#def mainturn(row, column):
# Try to avoid using globals. We'll store these in our class
#global countmove
#countmove = countmove + 1
#global symbol
#grid[row][column] = symbol
#for y in range(0,(len(grid))):
# print(grid[y])
#if symbol == 'X':
# symbol = 'O'
#elif symbol == 'O':
# symbol = 'X'
#return countmove
# This is one heck of an if statement. Lets turn it into a function
# if (grid[0][0] and grid[0][1] and grid[0][2] == symbol) or (grid[1][0] and grid[1][1] and grid[1][2] == symbol) or (grid[2][0] and grid[2][1] and grid[2][2] == symbol) or (grid[0][0] and grid[1][0] and grid[2][0] == symbol) or (grid[0][1] and grid[1][1] and grid[2][1] == symbol)or (grid[0][2] and grid[1][2] and grid[2][2] == symbol)or (grid[0][0] and grid[1][1] and grid[2][2] == symbol) or (grid[2][0] and grid[1][1] and grid[0][2] == symbol):
def check_win(self, symbol):
combinations = [
# horizontal
[(0,0), (1,0), (2,0)],
[(0,1), (1,1), (2,1)],
[(0,2), (1,2), (2,2)],
# vertical
[(0,0), (0,1), (0,2)],
[(1,0), (1,1), (1,2)],
[(2,0), (2,1), (2,2)],
# crossed
[(0,0), (1,1), (2,2)],
[(2,0), (1,1), (0,2)]
]
for coordinates in combinations:
letters = [self.game_board[x][y] for x, y in coordinates]
# If all the letters match
if "." not in letters:
if len(set(letters)) <= 1:
# returns corresponding letter for winner (X/O)
print("Well done {}! You won the game!".format(symbol))
self.running = False
return True
return False
# Lets try another method of checking if the board is full
#elif countmove == 9:
# print("Board Full. Game over.")
#main program
def board_full(self):
for row in self.game_board:
if "." in row:
return False
print("The game is a draw :( ")
# Stop the game
self.running = False
return True
def run_game(self):
# While the game is not over
while self.running != False:
# Show the player the board
self.show_board()
row, column = self.get_move()
# Is the move valid?
if self.check_move(row, column):
self.game_board[row][column] = self.active_player
# Did they win?
self.check_win(self.active_player)
# Change Players
if self.active_player == self.player_one:
self.active_player = self.player_two
else:
self.active_player = self.player_one
# Print the winning game board
self.show_board()
g = Game("X", "O")
# Handled this in the class
#grid = [["","",""],["","",""],["","",""]]
#countmove = 0
#win = 'false'
# Turned this code into the show_board function
#for y in range(0,(len(grid))):
#print(grid[y])
#symbol = start_player()
#while countmove != 9 or win == 'false':
# Shouldnt reset the countmove inside of the loop thats checking the countmove
#countmove = 0
#row, column = getmove()
#mainturn(row,column)
#win = check_win(row,column, symbol)
Example Output:
Player One (X) will start the game.
. . .
. . .
. . .
Player (X) please choose a Column and a Row: 0 1
. X .
. . .
. . .
Player (O) please choose a Column and a Row: 2 2
. X .
. . .
. . O
Player (X) please choose a Column and a Row: 0 0
X X .
. . .
. . O
Player (O) please choose a Column and a Row: 2 1
X X .
. . .
. O O
Player (X) please choose a Column and a Row: 0 2
Well done X! You won the game!
X X X
. . .
. O O

Python: Trying to program a variant of connect four: Winning condition doesn't stop

I have been trying to program a variant of connect four for my programming class. The board is 6x8 in size. In the variant I'm trying to program, the winning condition is to essentially build an L.
This means any construction of the form
X
X
X X
is a winning condition.
I have been trying to make a function that checks every single column for the same symbol consecutively to build a pair. And a function to do the same for every row. With these two functions I would then check if 2 pairs are consecutive, because no matter how you combine a vertical and horizontal pair, it will always build an 'L'.
To make a clear board I'm using
def ClearBoardSingle():
global Board
Board = [['0' for i in range(8)] for i in range(6)]
BoardPrint()
PlayerMoveSingle()
And for my interface I'm using
def BoardPrint():
global Board
global GameMoves
global PlayerTurn
global Player1Symbol
global Player2Symbol
print('\n\nMoves done: ' + str(GameMoves))
print('To Restart: R | To Quit: Q')
print('Valid choices: 1, 2, 3, 4, 5, 6, 7, 8')
if PlayerTurn == 0:
print('It\'s ' +str(Player1Symbol) + '\'s Turn')
if PlayerTurn == 1:
print('It\'s ' +str(Player2Symbol) + '\'s Turn')
print(Board[0])
print(Board[1])
print(Board[2])
print(Board[3])
print(Board[4])
print(Board[5])
I already figured out how to change Variables inside the Board, and I'm pretty much done. The only thing I don't know how to implement is the winning condition. I tried this function for the Rows:
def VerticalList(Column):
global Board
global Choice
global Row0
Column = int(Column)
Choice = int(Choice)
print(Column,' C')
while Column > 0:
for Board[Column][Choice] in range(Column):
Row0.append(Board[Column][Choice])
if Column ==6 or Column == -1:
break
else:
VerticalList(Column-1)
if Column ==0:
break
else:
continue
if Column == 0:
Column += 1
while Column < 5:
Column +=1
if Row0[Column] == Row0[Column-1]:
print('Pair')
else:
print('No Pair')
pass
else:
pass
But it enters an endless Loop.
I have no ideas anymore on how to implement the winning condition. I'd appreciate any kind of help or ideas. If you want me to post the whole code or other kinds of snippets, ask for them.
Thank you in anticipation!
Cool problem, below looks like a lot of code, but it's not really. I haven't checked this extensively, so I'm not confident that it doesn't find false positives, but it seems to find L's that it should be finding. The main thing I did was use itertools.combinations to take all 4-sized groups of the positions of X's and then check if they looked like patterns I was expecting for L's. In check_four_group I look at the differences within the row and columns.
from itertools import combinations
def disp_board(board):
for row in board:
print(row)
def check_winning(board):
winning = False
#Find all row,col positions of the X's
x_poses = [(i,j) for i in range(6) for j in range(8) if board[i][j] == 'X']
#Loop through every combination of four X's since it takes four to make the 'L'
for group in combinations(x_poses,4):
if(check_four_group(group)):
winning = True
break
return winning
def check_four_group(group):
rows,cols = zip(*[(r,c) for r,c in group])
row_diffs = [rows[i+1]-rows[i] for i in range(len(rows)-1)]
col_diffs = [cols[i+1]-cols[i] for i in range(len(cols)-1)]
#Uncomment this to print the row and col diffs
#print(row_diffs)
#print(col_diffs)
# Finds:
# X
# X
# X X
if row_diffs == [1,1,0] and col_diffs == [0,0,1]:
return True
# Finds:
# X
# X
# X X
elif row_diffs == [1,1,0] and col_diffs == [0,-1,1]:
return True
# Finds:
# X X
# X
# X
elif row_diffs == [0,1,1] and col_diffs == [1,0,0]:
return True
# Finds:
# X X
# X
# X
elif row_diffs == [0,1,1] and col_diffs == [1,-1,0]:
return True
# Otherwise it's not there at all (not thinking about horizontal L's but could add that)
else:
return False
#Test case 1
def test_case_1():
board = [['0' for i in range(8)] for i in range(6)]
board[2][1] = 'X'
board[2][2] = 'X'
board[3][1] = 'X'
board[4][1] = 'X'
return board
#Test case 2
def test_case_2():
board = [['0' for i in range(8)] for i in range(6)]
board[2][1] = 'X'
board[2][0] = 'X'
board[3][1] = 'X'
board[4][1] = 'X'
return board
#Test case 3
def test_case_3():
board = [['0' for i in range(8)] for i in range(6)]
board[1][0] = 'X'
board[2][0] = 'X'
board[3][0] = 'X'
board[3][1] = 'X'
return board
#Test case 4
def test_case_4():
board = [['0' for i in range(8)] for i in range(6)]
board[1][2] = 'X'
board[2][2] = 'X'
board[3][2] = 'X'
board[3][1] = 'X'
return board
##################
#Start of program#
##################
board = test_case_1()
#board = test_case_2()
#board = test_case_3()
#board = test_case_4()
disp_board(board)
if check_winning(board):
print('Victory')
else:
print('Keep playing')

In over my head: Debugging and many errors

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()

Categories

Resources