I am a beginner with python, and I am creating a two player tic tac toe game in terminal. In total, this code takes up 139 lines, (this below being the relevant part of code I am having trouble with), however, this CheckWin function takes up around 40 lines of code, which I think is quite a lot compared to the amount of lines in this code, and considering that it performs a somewhat basic function. Basically, in the game, this function checks whether a row, column, or diagonal, has three X's or three O's, and if it does, it assigns X to the winner and O to the winner. Anyway, here is the code.
X = "X"
O = "O"
empty = " "
S = [" ", " ", " ", " ", " ", " ", " ", " ", " "]
def CheckWin(S):
global winner
winner = ""
if S[0] == S[1] == S[2] != empty:
if S[0] == X:
winner = X
if S[0] == O:
winner = O
if S[3] == S[4] == S[5] != empty:
if S[3] == X:
winner = X
if S[3] == O:
winner = O
if S[6] == S[7] == S[8] != empty:
if S[6] == X:
winner = X
if S[6] == O:
winner = O
if S[0] == S[3] == S[6] != empty:
if S[0] == X:
winner = X
if S[0] == O:
winner = O
if S[1] == S[4] == S[7] != empty:
if S[1] == X:
winner = X
if S[1] == O:
winner = O
if S[2] == S[5] == S[8] != empty:
if S[2] == X:
winner = X
if S[2] == O:
winner = O
if S[0] == S[4] == S[8] != empty:
if S[0] == X:
winner = X
if S[0] == O:
winner = O
if S[2] == S[4] == S[6] != empty:
if S[2] == X:
winner = X
if S[2] == O:
winner = O
Basically, I need help making the function much much simpler. However, I do not want to eliminate the X, O, and winner variables, nor do I want to eliminate the list index method with the list S. Even though, is there a way to simplify all these If statements, keeping these things? If so, how?
Your code looks for "trios" of positions; you might as well have an object that holds this info:
trios = ((0,1,2), (3,4,5), (6,7,8), (0,3,6), (1,4,7), (2,5,8), (0,4,8), (2,4,6))
Then CheckWin would just loop through every trio, do that check you're doing, and return a winner if the trio matches. This way, CheckWin would be less than 10 lines of code.
I don't want to give it all away because I'm sure you can do it :)
Also, you don't need a global variable called "winner" inside CheckWin; just have CheckWin return the winner (or ""), and store the result in a variable outside the function itself.
I.e.
winner = CheckWin(S)
Have you tried using a loop instead?
X, O = 'X', 'O'
S = [X,O,X,O,X,O,O,X,O] # Test values
def CheckWin(S):
index = 0
has_winner = False
while index < len(S):
print index
if index <= 6: # after this point you won't find a winner (or you run this after every turn?)
if (index % 3 == 0 and S[index] == S[index + 1] and S[index] == S[index + 2]): # horizontals
has_winner = True
elif index < 3: # verticals and diagonals (you only need the first row to know this)
if (S[index] == S[(index + 3)] and S[index] == S[index + 6]) or \
(index == 0 and S[index] == S[4] and S[index] == S[8]) or \
(index == 2 and S[index] == S[4] and S[index] == S[6]):
has_winner = True
if has_winner: # I'm using this to prevent duplicating code above (one if less)
if S[index] in [X,O]:
return S[index]
index += 1
return has_winner # If we have a winner but the X or O criterion isn't met, it returns False
winner = CheckWin(S)
Related
This is the main question
Write a function named find_longest_string(legoString,n) that finds and returns the longest
diagonal string from the upper-right triangle of the matrix representation of the brick placement.
The longest diagonal string is defined as a string that contains the maximum occurrences of a
letter on the same diagonal. In case there are multiple solutions i.e. diagonal strings of the
same maximum lengths, you can return any of the valid solutions. Fig 5 shows a computation
of a valid solution. This function must have two parameters, legoString - a string returned from
the function named “place_random_bricks”, and n - the number of columns on the baseplate.
In this example the letter G would be occurring the most
def find_longest_string(legoString, n):
list2 = []
countr = ""
countb = ""
countg = ""
county = ""
countc = ""
for i in range(len(legoString)):
list = []
if i % n == 0: #guarantees that row will only have required amount of columns(no more no less)
sub = legoString[i: i + n] #i starts at 0 and so add number of columns for first row and continue
#list = [] #create empty list each time a row is created
for j in sub:
list.append(j)
list2.append(j)
answer = ''.join(''.join(tup) for tup in list)
answer2 = ''.join(''.join(tup) for tup in list)
print(answer) # prints list before it is reset
for index in range(len(answer)):
if answer2[index] == answer[index-1]:
if answer2[index] == "R":
countr = countr + "R"
elif answer2[index] == "B":
countb = countb + "B"
elif answer2[index] == "G":
countg = countg + "G"
elif answer2[index] == "Y":
county = county + "Y"
elif answer2[index] == "C":
countc = countc + "C"
print(countr, countc, county, countb, countg)
if len(countr) >= len(countb) and len(countg) and len(county) and len(countc):
return countr
elif len(countb) >= len(countr) and len(countg) and len(county) and len(countc):
return countb
elif len(countg) >= len(countr) and len(countb) and len(county) and len(countc):
return countg
elif len(countc) >= len(countr) and len(countg) and len(county) and len(countb):
return countc
elif len(county) >= len(countr) and len(countg) and len(countb) and len(countc):
return county
I am making a connect four game with X's and O's. The code for checking for four in a row/column/diagonal works but I have a lot of if statements in my code. The game fully works right now but I'm wondering if there is an easier solution to the checking. Below, I have included all my code for context.
I have tried using coordinates. It seems kind of inefficient though. The function for checking is called check.
namex = input("Player X, enter your name. ") #asks for player 1 name
nameo = input("Player O, enter your name. ") #asks for player 2 name
game = [[".", ".", ".", ".", ".", "."], #gameboard
[".", ".", ".", ".", ".", "."],
[".", ".", ".", ".", ".", "."],
[".", ".", ".", ".", ".", "."],
[".", ".", ".", ".", ".", "."],
[".", ".", ".", ".", ".", "."],
[".", ".", ".", ".", ".", "."]]
loop = True
def output(matrix):
str1 = ""
str2 = ""
str3 = ""
str4 = ""
str5 = ""
str6 = ""
print("0 1 2 3 4 5 6 ") #print labels for columns in gameboard
for a in matrix:
row = 0
for b in a: #"a" is a column and "b" is a ./x/o
row += 1
if row == 1:
str1 += b
str1 += " "
if row == 2:
str2 += b
str2 += " "
if row == 3:
str3 += b
str3 += " "
if row == 4:
str4 += b
str4 += " "
if row == 5:
str5 += b
str5 += " "
if row == 6:
str6 += b
str6 += " "
print(str1) #print string for row 1
print(str2) #print string for row 2
print(str3) #print string for row 3
print(str4) #print string for row 4
print(str5) #print string for row 5
print(str6) #print string for row 6
def check(matrix): #function to check for four in row/column/diagonal to win
positionx = []
positiono = []
x = 0
for a in matrix:
y = 5
for b in a:
if b == "X":
positionx.append([x, y])
if b == "O":
positiono.append([x, y])
y -= 1
x += 1
for c1 in positionx:
'''check four in row/column/diagonal for x'''
for c2 in positionx:
for c3 in positionx:
for c4 in positionx:
if c4[0]-c3[0] == 1:#check for four in row
if c3[0]-c2[0] == 1:
if c2[0]-c1[0] == 1:
return "xwin"
if c4[1]-c3[1] == 1: #check for four in column
if c3[1]-c2[1] == 1:
if c2[1]-c1[1] == 1:
return "xwin"
if c4[0]-c3[0] == 1: #check four in diagonal
if c4[1]-c3[1] == 1:
if c3[0]-c2[0] == 1:
if c3[1]-c2[1] == 1:
if c2[0]-c1[0] == 1:
if c2[1]-c1[1] == 1:
return "xwin"
for d1 in positiono:
'''check four in row/column/diagonal for o'''
for d2 in positiono:
for d3 in positiono:
for d4 in positiono:
if d4[0]-d3[0] == 1: #check for four in row
if d3[0]-d2[0] == 1:
if d2[0]-d1[0] == 1:
return "owin"
if d4[1]-d3[1] == 1: #check for four in column
if d3[1]-d2[1] == 1:
if d2[1]-d1[1] == 1:
return "owin"
if d4[0]-d3[0] == 1: #check four in diagonal
if d4[1]-d3[1] == 1:
if d3[0]-d2[0] == 1:
if d3[1]-d2[1] == 1:
if d2[0]-d1[0] == 1:
if d2[1]-d1[1] == 1:
return "owin"
while loop == True:
xinput = input(namex + ", you're X. What column do you want to play in? Please enter a number 0-6 ")
xcolumn = int(xinput)
xrow = 5
occupied1 = False
while occupied1 == False:
if game[xcolumn][xrow] == ".": #if there is a "." change to "X"
game[xcolumn][xrow] = "X"
output(game)
occupied1 = True
xrow -= 1
if check(game) == "xwin":
loop = False
print(namex + " wins!")
break
if check(game) == "owin":
loop = False
print(nameo + " wins!")
break
oinput = input(nameo + ", you're O. What column do you want to play in? Please enter number 0-6 ")
ocolumn = int(oinput)
orow = 5
occupied2 = False
while occupied2 == False:
if game[ocolumn][orow] == ".": #if there is a "." change to "O"
game[ocolumn][orow] = "O"
output(game)
occupied2 = True
orow -= 1
if check(game) == "xwin":
loop = False
print(namex + " wins!")
break
if check(game) == "owin":
loop = False
print(nameo + " wins!")
break
I'm also open to any other suggestions to make my code for this game better. Thanks!
I had some spare time, so I rewrote your program. It's much more efficient now. Read the comments to understand how it works
cols = [[] for x in range(6)]
# I opted to have a matrix of COLUMNS rather than rows because you can easily
# append items to the end of the list to simulate a real tile being placed there
# it's more intuitive and saves us time, as you'll see
def checkWin(cols):
for i in range(6): # Each column
for j in range(6): # Each row
try: #check if the element at these coordinates exists yet
cols[i][j]
except IndexError:
break
# go back to next i - impossible that there's anything with a higher
# j because if a list is n items long, and we go to j (which is one
# higher than n and doesn't exist) then there can't be an element at
# index j + someNumber.
ret = False
try: #vertical: j is the index in each column, so this goes up the column
if cols[i][j] == cols[i][j+1] == cols[i][j+2] == cols[i][j+3] is not None:
ret = True
except IndexError: #one of the elements of the comparison doesn't exist
pass #We can't be sure that none of the other trials will return True
try: #horizontal
if cols[i][j] == cols[i+1][j] == cols[i+2][j] == cols[i+3][j] is not None:
ret = True
except IndexError:
pass
try: #diagonal
if cols[i][j] == cols[i+1][j+1] == cols[i+2][j+2] == cols[i+3][j+3] is not None:
ret = True
except IndexError:
pass
try: #other diagonal
if cols[i][j] == cols[i-1][j+1] == cols[i-2][j+2] == cols[i-3][j+3] is not None:
ret = True
except IndexError:
pass
if ret:
return cols[i][j]
return None # We've gone through every single possible element and there are NO matches
def printBoard(cols):
# Pretty intuitive function IMO - we swap i and j to go through rows.
returnstr = '\n1 2 3 4 5 6\n'
for i in range(6):
for j in range(6):
try:
cols[j][5-i]
except IndexError:
returnstr += '_ '
continue
returnstr += cols[j][5-i]+' '
returnstr += '\n'
print(returnstr)
playerX = input('Player X, input your name: ')
playerO = input('Player O, input your name: ')
if playerX == playerO:
print("Cannot have the same name!")
exit()
count = 0
while not checkWin(cols):
printBoard(cols)
i = input('{}, input your column (1-6): '.format(playerO if count else playerX))
try:
target = cols[int(i)-1]
if len(target) == 6:
print("Column {} is already full! Please pick another.".format(i))
continue
target.append('O' if count else 'X')
except ValueError:
print("'{}' is not a number! Try again.".format(i))
continue
except IndexError:
print("{} is not a valid column number! Please pick another.".format(i))
continue
count = (count+1) % 2
printBoard(cols)
if checkWin(cols) == 'X':
print('{} (Player X), you win!'.format(playerX))
else:
print('{} (Player O), you win!'.format(playerO))
A good start would be to write a generalized function that checks for a diagonal at an arbitrary location:
def diagonal(grid, x, y, piece):
'''
Return True if grid contains a four-in-a-row diagonal starting at coordinates
(x, y) and traveling downwards and to the right. Otherwise return False.
'''
for i in range(x, x+4):
# if this square does not contain the desired piece, return False
if grid[x+i][y+i] != piece
return False
# if we got all the way through the loop, this must be a diagonal
return True
Then you would call this function for every possible starting coordinate of a four square diagonal, for each player X and O.
To improve this function, you could add a way to check for diagonals that travel the other direction (up and to the right).
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')
I'm trying to write a version of always popular Count-A-WFF section of the WFF 'N Proof game (no copyright infringement intended) in Python. Alright, not so popular.
I think I have everything up and running up as desired for up to the case of a 4 letter string.
def maximum_string(s):
if cs(s) == True:
return len(s)
elif len(s) == 2:
l1 = [cs(s[0]), cs(s[1])]
if True in l1:
return len(s) - 1
else:
return 0
elif len(s) == 3:
first = s[0] + s[1]
second = s[0] + s[2]
third = s[1] + s[2]
l1 = [cs(first), cs(second), cs(third)]
if True in l1:
return len(s) - 1
l2 = [cs(s[0]), cs(s[1]), cs(s[2])]
if True in l2:
return len(s) - 2
else:
return 0
elif len(s) == 4:
first = s[0]+s[1]+s[2]
second = s[0]+s[1]+s[3]
third = s[1]+s[2]+s[3]
fourth = s[0]+s[2]+s[3]
l1 = [cs(first), cs(second), cs(third), cs(fourth)]
if True in l1:
return 3
first = s[0] + s[1]
second = s[0] + s[2]
third = s[0] + s[3]
fourth = s[1] + s[2]
fifth = s[1] + s[3]
sixth = s[2] + s[3]
l2 = [cs(first), cs(second), cs(third), cs(fourth), cs(fifth), cs(sixth)]
if True in l2:
return 2
first = s[0]
second = s[1]
third = s[2]
fourth = s[3]
l3 = [cs(first), cs(second), cs(third), cs(fourth)]
if True in l3:
return 1
else:
return 0
def cs(string):
global length_counter, counter, letter
counter = 1
length_counter = 0
letters_left = len(string)
while letters_left != 0 and length_counter < len(string):
letter = string[length_counter]
if letter == 'C' or letter == 'A' or letter == 'K' or letter == 'E' or letter == "K":
counter += 1
elif letter == 'N':
counter += 0
else:
counter -= 1
length_counter += 1
letters_left -= 1
if counter == 0 and len(string) == length_counter:
return True
else:
return False
The maximum_string helper function is intended to, given any string S, find the length of one of the longest possible wffs that you can make from just the letters of S. Of course, I can continue the pattern I currently have for the maximum_string helper function up to a length of 13. But, combinatorial explosion is evident. Thus, is there a more elegant way to finish off the maximum string helper function?
In effect one of the functions I had earlier would return a distance of how far away a string is from having a permutation in Polish notation. Thus this was surprisingly simpler to fix than I expected. Here's what I was looking for:
def maximum_string(string):
global length_counter, counter, letter
counter = 1
length_counter = 0
letters_left = len(string)
while letters_left != 0 and length_counter < len(string):
letter = string[length_counter]
if letter == 'C' or letter == 'A' or letter == 'K' or letter == 'E' or letter == "K":
counter += 1
elif letter == 'N':
counter += 0
else:
counter -= 1
length_counter += 1
letters_left -= 1
if ('p' in string) or ('q' in string) or ('r' in string) or ('s' in string) or ('t' in string) or ('u' in string):
return len(string) - abs(counter)
else:
return 0
I have coded a game of noughts and crosses, and am using a while loop to run the game. However, I must be doing something wrong as I cannot get it to print 'You win' or 'You lose' when a row/column/diagonal of X or O is on the board. I know the function that checks the board works as I have tested it on its own, by putting Xs in the board manually, but when playing the game normally it completely disregards any Xs or Os in 3. Here is the code, sorry it's abit long. Thanks
import random
board = [
['1','-','-','-'],
['2','-','-','-'],
['3','-','-','-'],
['#','1','2','3'],
[' ',' ',' ',' ']
]
rows = {
'top':
board[0][1:],
'mid':
board[1][1:],
'bottom':
board[2][1:]
}
cols = {
'left':
[board[0][1],
board[1][1],
board[2][1]],
'mid':
[board[0][2],
board[1][2],
board[2][2]],
'right':
[board[0][3],
board[1][3],
board[2][3]]
}
diags = {
'top-bottom':
[board[0][1],
board[1][2],
board[2][3]],
'bottom-top':
[board[2][1],
board[1][2],
board[0][3]]
}
gamestate = 1
def print_board(board):
for i in board:
print " ".join(i)
def win_check(rows,cols,diags):
plrWin = ['X','X','X']
cpuWin = ['O','O','O']
global gamestate
for i in rows.values():
if i == plrWin:
return True
gamestate = 0
elif i == cpuWin:
return False
gamestate = 0
for x in cols.values():
if x == plrWin:
return True
gamestate = 0
elif x == cpuWin:
return False
gamestate = 0
for y in diags.values():
if y == plrWin:
return True
gamestate = 0
elif y == cpuWin:
return False
gamestate = 0
def game_entry():
print "Your turn."
coordY = input("Guess column: ")
coordX = input("Guess row: ")
board[coordX - 1][coordY] = 'X'
def random_location():
while True:
cpuX = random.randint(1,3)
cpuY = random.randint(1,3)
if (board[cpuX - 1][cpuY] == 'X') or (board[cpuX - 1][cpuY] == 'O'):
continue
else:
board[cpuX - 1][cpuY] = 'O'
break
while gamestate == 1:
print_board(board)
game_entry()
random_location()
if win_check(rows,cols,diags) == True:
print "You win!"
gamestate = 0
break
elif win_check(rows,cols,diags) == False:
print "You lose."
gamestate = 0
break
else:
continue
Your problem is with all of the rows and cols dictionaries:
>>> l = [[1, 2, 3], [4, 5, 6]]
>>> x = l[0][1:]
>>> x
[2, 3]
>>> l[0][1] = 4
>>> x
[2, 3]
As you can see, they don't update when the board is changed. You'll have to find another way of doing this.
I would just use a few loops and check the diagonals manually:
def has_someone_won(board):
# Rows
for row in board:
if row[0] == row[1] == row[2] != '-':
return True
# Columns
for i in range(3):
if board[0][i] == board[1][i] == board[2][i] != '-':
return True
# Diagonal 1
if board[0][0] == board[1][1] == board[2][2] != '-':
return True
# Diagonal 2
if board[2][0] == board[1][1] == board[0][2] != '-':
return True
# There's no winner
return False