I'm coding a bot in Python that plays tic-tac-toe. The game is a Web app written in React.js and is equipped with an AI of its own that utilizes minimax. The user (which the Python bot simulates) is always X, the AI is always O, and the user always moves first. The Python bot plays by randomly selecting unmarked squares (this is only to demonstrate automated UI testing).
I was getting stuck inside a recursive function.
for loop and recursive call:
for i in clickedSquares:
if not winner:
self.checkForWinner(load_browser, winner)
elif i == random_square:
self.test_playTTT(load_browser)
else:
clickedSquares.append(random_square)
To fix this issue I added the if not winner condition where "winner" is a string. This does terminate the loop; however, I'm getting an error as soon as the checkForWinner() function is called because winnerOh is always false.
winnerOh = WebDriverWait(load_browser, 10).until(EC.presence_of_element_located((By.XPATH, Tags.resultOh)))
winnerEx = WebDriverWait(load_browser, 10).until(EC.presence_of_element_located((By.XPATH, Tags.resultEx)))
tiedGame = WebDriverWait(load_browser, 10).until(EC.presence_of_element_located((By.XPATH, Tags.resultTie)))
I'm looking for an element on the UI that declares the winner: X or O or tie, which will only appear if the game is over. So WebDriverWait().until() is timing out waiting for that element to appear, but it hasn't yet, because it's only the second move in the game.
I'm not sure how to fix this issue. If I remove the call to checkForWinner() the bot will get stuck in the recursive call to test_playTTT(). The browser will not close after the game is over, and the test will not end successfully.
Is there another way to check the UI for the element I'm looking for that won't immediately return a False condition? Is there a better way for me to write this for loop?
Included below is the full Python method.
class TestCase_PlayRandom():
URL = "http://localhost:3000"
#pytest.fixture
def load_browser(self, browser):
browser.get(self.URL)
yield browser
def test_playTTT(self, load_browser):
squares = [Tags.square1,Tags.square2,Tags.square3,
Tags.square4,Tags.square5,Tags.square6,
Tags.square7,Tags.square8,Tags.square9]
clickedSquares = []
random_square = randint(0,8)
time.sleep(5)
winner = ''
if not clickedSquares:
element = load_browser.find_element(By.XPATH, squares[random_square])
element.click()
clickedSquares.append(random_square)
for i in range(0,9):
if squares[i] == Tags.ohSquare:
clickedSquares.append(i)
for i in clickedSquares:
if not winner:
self.checkForWinner(load_browser, winner)
elif i == random_square:
self.test_playTTT(load_browser)
else:
clickedSquares.append(random_square)
element = load_browser.find_element(By.XPATH, squares[random_square])
element.click()
def checkForWinner(self, load_browser, winner):
winnerOh = WebDriverWait(load_browser, 10).until(EC.presence_of_element_located((By.XPATH, Tags.resultOh)))
winnerEx = WebDriverWait(load_browser, 10).until(EC.presence_of_element_located((By.XPATH, Tags.resultEx)))
tiedGame = WebDriverWait(load_browser, 10).until(EC.presence_of_element_located((By.XPATH, Tags.resultTie)))
if winnerOh:
winner = 'O'
LOGGER.info('Winner O')
return winner
elif winnerEx:
winner = 'X'
LOGGER.info('Winner X')
return winner
elif tiedGame:
winner = 'None'
LOGGER.info('Tie')
return winner
Try this:
def checkForWinner(self, load_browser, winner):
winnerOh = WebDriverWait(load_browser, 10).until(EC.invisibility_of_element_located((By.XPATH, Tags.resultOh)))
winnerEx = WebDriverWait(load_browser, 10).until(EC.invisibility_of_element_located((By.XPATH, Tags.resultEx)))
tiedGame = WebDriverWait(load_browser, 10).until(EC.invisibility_of_element_located((By.XPATH, Tags.resultTie)))
if not winnerOh:
winner = 'O'
LOGGER.info('Winner O')
return winner
elif not winnerEx:
winner = 'X'
LOGGER.info('Winner X')
return winner
elif not tiedGame:
winner = 'None'
LOGGER.info('Tie')
return winner
invisibility_of_element_located() will return True if the element is not found on the UI and False if it is.
You'll need to play with where to put the call to the function. I'd place it above the for loop instead of in it.
Related
I made a code to scrape some website. A list of IDs is iterated in the website, and it contains two conditions(If and Elif). But the problem is with the Elif. The error is it doesn't found the elif element (elem2).
I read in this question Python if elif else can't go to the elif statement Selenium the solution is a try/except, butI already used a Try/except to make works the if statement. What is a solution to make this code works with two conditions?
The code looks like this:
for item in list:
input = driver.find_element(By.ID, "busquedaRucId")
input.send_keys(item)
time.sleep(2)
elem1 = driver.find_element(By.ID, 'elem1')
elem1_displayed = elem1.is_displayed()
elem2 = driver.find_element(By.ID, 'elem2')
elem2_displayed = elem2.is_displayed()
try:
if elem1_displayed is True:
code to scrape given de first condition
elif elem2_displayed is True:
code to scrape given de second condition
except NoSuchElementException:
input = driver.find_element(By.ID, ('busquedaRucId')).clear()
Than you for any help. I'm stuck with this problem for two weeks.
I would restructure your code by wrapping the find_element function in a function which handles NoSuchElementExceptions by returning False, basically making the error silent:
def element_exists_and_displayed(driver, id):
try:
return driver.find_element(By.ID, id).is_displayed()
except NoSuchElementException:
return False
for item in list:
input = driver.find_element(By.ID, "busquedaRucId")
input.send_keys(item)
time.sleep(2)
if element_exists_and_displayed(driver, 'elem1'):
# code to scrape given first condition
pass
elif element_exists_and_displayed(driver, 'elem2'):
# code to scrape given second condition
pass
else:
driver.find_element(By.ID, ('busquedaRucId')).clear()
I have the following code and for some reason it doesn't quit the browser when the two elif statements are triggered OR go to the except part of the code.
The goal is to return False in every case where the "Solved" attribute is not found. I have tried removing the elif parts so that it would go to except part in all of the other cases but it still doesn't work.
while True:
try:
sleep(10)
status = browser.find_element(By.CLASS_NAME, 'status')
if status.get_attribute("innerHTML") == "Solved":
break
elif status.get_attribute("innerHTML") == "Unknown error, watch console":
browser.quit()
print("Unknown error - programm ootab 3 minutit...\n")
sleep(180)
return False
elif status.get_attribute("innerHTML") == "Outdated, should be solved again":
browser.quit()
print("Captcha outdated - programm ootab 3 minutit...\n")
sleep(180)
return False
except:
print('Captcha fked up for some reason \n')
browser.quit()
return False
I have tried to create a BLACKJACK game using python (actually I'm learning python). Currently I have not setup bet command (that is written in my Account class). I only takes name from my Account class.
I have a main file : blackjack.py
and two classes in files : deckofcards.py, account.py
I am only accessing name from account class, so I wont be putting that long mess here.
blackjack.py :
from account import Account
player = Account('kalaLokia')
cards = DeckOfCards()
play = False
playershand = []
dealershand = []
action = ''
blackjack = False
def showCards(items, name):
'''
Shows {name}'s cards and hand value
'''
print(f"{name}'s hand: ")
print(f"\t{' - '.join(items)}")
print(f"Hand value: {cards.handValue(items)}")
def bust(hand):
'''
Whether a someone has busted or not
'''
if(cards.handValue(hand) > 21):
return True
return False
def dealersMove():
'''
Dealers move: executes when player calls "stand"
Dealer perform hit until he gets bust, wins or his hand value becomes >= 17
When hand value is >17 and players has greater value, dealer loses ;-)
'''
global blackjack
if(cards.handValue(dealershand) == 21):
print('Dealer got a BLACKJACK')
print('Dealer WINS')
return
elif(blackjack):
print(f'{player.name} got a BLACKJACK')
print(f'{player.name} WINS')
blackjack=False
return
while(not bust(dealershand)):
if(cards.handValue(dealershand) > cards.handValue(playershand)):
print('Dealer WINS')
showCards(dealershand, 'Dealer')
break
elif(cards.handValue(dealershand) == cards.handValue(playershand)):
print("It's a TIE!!\n Dealer WINS")
break
elif(cards.handValue(dealershand) > 17):
print(f'Dealer loses\n{player.name} has WON.')
print(f'{cards.handValue(playershand)} > {cards.handValue(dealershand)}')
break
dealershand.append(cards.hit())
else:
print(f'Dealer busts! \n{player.name} has WON the game.')
def start():
'''
The actiona that can be performed
'''
global blackjack
if(cards.handValue(playershand) == 21):
blackjack = True
dealersMove()
return
while(not bust(playershand)):
action = input(
f"{player.name}'s turn: Do you want to hit or stand ? ").lower()
if(action == 'hit'):
playershand.append(cards.hit())
showCards(playershand, player.name)
elif(action == 'stand'):
dealersMove()
break
else:
print('Please enter a valid action !')
else:
print(f'{player.name} has been BUSTED')
if __name__ == "__main__":
print(f'Hello {player.name}, Welcome to BlackJack Game')
# Tell game rules here, may be
response = input('Do you want to start the game (Y/n)? ').lower()
if(response != 'y'):
play = False
print('You have been exited the game')
else:
play = True
# Ask for bet amount later
while(play):
cards = DeckOfCards()
cards.shuffle()
print('Cards on the table is now shuffled')
playershand = list(cards.initiate())
dealershand = list(cards.initiate())
print(
f"{player.name}'s hand:\n {playershand[0]} - {playershand[1]}\nHand value: {cards.handValue(playershand)}\n")
print(f"Dealer's hand:\n {dealershand[0]} - ?\n")
start()
if(input('Do you want to play again (Y/n)?').lower() != 'y'):
print('The End')
play = False
deckofcards.py :
import random
class DeckOfCards():
'''
All talks here is about cards
'''
cards = {'A':11,'K':10,'Q':10,'J':10,'2':2,'3':3,'4':4,'5':5,'6':6,'7':7,'8':8,'9':9,'10':10}
def __init__(self):
'''
Initialize deck of cards
'''
self.deck = list(self.cards.keys())*4
def shuffle(self):
'''
Simply shuffles the deck of cards
'''
return random.shuffle(self.deck)
def handValue(self, hand):
'''
Calculates and returns the hand value, expecting a string value to be feeded.
'''
result = 0
for element in hand:
result = result + self.cards[element]
while('A' in hand and result > 21):
if(hand[0]=='A'):
result = result - 10
# Somehow this hand.pop is poping out from main value itself. Why ???
hand.pop(0)
if(hand == []):
break
return result
def hit(self):
'''
Pop out and returns the last card in the deck
'''
return self.deck.pop()
def initiate(self):
'''
Pop out 2 cards from the deck and return as a tuple
'''
return (self.deck.pop(), self.deck.pop() )
Issue:
When I have an ACE in my hand and my hand value is greater than 21, the while condition executes in the handValue function (which is in DeckofCards class) as it is. Problem is, after that while condition executes, playershand (declared in main file) I just passed to this handValue function gets empty. That is the hand.pop(0) actually seems popping out value from main object playershand itself (It seems me so).
When I press a hit after that, I get a single new card, all other cards are got popped out. I don't understand why it is so.
On hit(user enter hit) : actually I am passing playershand (cards on the player hand, it's a list) to function showCards (which is also in the main file) where it takes it as argument items and pass it to handValue function in the class DeckOfCards.
So why is it happening? even though I am passing playershand as an argument to other functions, how pop() function affecting playershand which has only access to hand object in the handValue class ?
I have my complete code in github repo to test out, the files in folder blackjack
how repeat code again again again this every work
I want the code below to always work and it should be repeated, and again this function should be repeated and not removed from the program.
def ref(self):
driver = self.driver
nextB2 = driver.find_element_by_xpath("""//section/span/button/span[#aria-label='Like']""")
nextB2.click()
time.sleep(5)
nextB3 = driver.find_element_by_xpath("""//section/span/button/span[#aria-label='Like']""")
nextB3.click()
time.sleep(6)
nextB4 = driver.find_element_by_xpath("""//section/span/button/span[#aria-label='Like']""")
nextB4.click()
time.sleep(7)
driver.refresh()
time.sleep(5)
driver.switch_to_frame('ref')
driver.refresh('ref')
you can use for loop with range to stop at perticular count like
for i in range(10): #10 times
ref() #function call
if you want it to run for ever
while True: #loop that never stops
ref()
you can use break and continue for conditional breaks
while True:
if foo == foo:
break #break or stop the while oop
elif foo == bar:
continue #skip current iteration and continue execution
else:
ref()
I'm making a tic tac toe game using tkinter for the GUI but my issue is, since i've got a function (that makes the moves) to run each time a button is clicked, it won't automatically make a move until I click a button (it doesn't matter which one) which hence stuffs up my moves indexing, and also means you have to click before the computer makes a move.
Is there anyway I can get the 'computer' itself to pretend to click a button, hence initiating the function?
Here's the make button bit:
def make_button(n, row, col):
button_list[n] = Button(tk,bg='gray', text = " ", fg='white',height=4,width=8,command=lambda:ttt(n, button_list))
button_list[n].grid(row=row,column=col,sticky = S+N+E+W)
and here's the make move bit (its wip so don't mind the fact that currently computer move will only play the middle or the corners):
def computer_move(buttons, index):
global bclick
buttons["text"] = "O"
COM.add(index)
bclick = True
buttons=StringVar()
bclick = True
corner_moves = {0,2,6,8}
def ttt(n, buttons):
global bclick
if n not in P and n not in COM and bclick == True:
button_list[n]["text"] = "X"
P.add(n)
bclick = False
elif bclick == False and 4 not in COM and 4 not in P:
computer_move(button_list[4], 4)
elif bclick == False:
corners_not_COM = (corner_moves.difference(COM))
corners_not_P_or_COM = (corners_not_COM.difference(P))
list_corners_not_P_or_COM = list(corners_not_P_or_COM)
random_corner_index = random.randint(0,len(list_corners_not_P_or_COM))
random_corner = list_corners_not_P_or_COM[random_corner_index]
computer_move(button_list[random_corner], random_corner)
Try using:
computer_move.invoke()
it has the same effect as clicking on the button it should work in your case. It calls the function. You can call it after you have moved and it will call computer_move for you. Happy coding!