I'm trying to create a simple game that creates math problems and the users task is to decide if they are true or false. (eg. 2 + 2 = 6, True or False?)
I am using the keyboard module and I want to have the user press the left arrow key if he thinks that the problem is true, and the right one if he thinks that it's false.
import random
import keyboard
def addition_easy():
x = random.randint(1, 6)
y = random.randint(1, 6)
z = x + y
answer_correct = random.choice([True, False])
if answer_correct == False:
answer = (random.randint(2, 12))
else:
answer = z
if answer == z:
answer_correct = True
print(f"{x} + {y} = {answer}")
print("True or False?")
while True:
if keyboard.is_pressed('left'):
user_answer = True
break
elif keyboard.is_pressed('right'):
user_answer = False
break
if user_answer == answer_correct:
return True
else:
return False
The thing is, after I paste this function into a loop, I can only press left or right once. After that the rest of the code is executed without waiting for my keypress.
from problems import addition_easy
exercise_amount = int(input("How many exercises would you like to solve?"))
for exercise in range(1, exercise_amount + 1):
addition_easy()
This returns (for input of 5):
How many exercises would you like to solve? 5
6 + 1 = 9
True or False? //(Here it waits for me to press "left" or "right")
3 + 3 = 8
True or False? //(From here it doesn't stop to wait for a keypress)
4 + 3 = 7
True or False? //(Same here and so on...)
2 + 3 = 3
True or False?
1 + 2 = 3
True or False?
How can I make it wait for a keypress every time it prints out a math problem?
If the user holds down "left" for half a second, and addition_easy executes a hundred times in that half second, then keyboard.is_pressed('left') will evaluate to True for every one of them, even though the user only pressed "left" once.
You can verify that is_pressed doesn't permanently consider "left" to be pressed by telling your program to do 1000 problems. Pressing left will only answer about 20 of them.
One possible solution is to alter your loop so it waits until the key is subsequently released before continuing.
while True:
if keyboard.is_pressed('left'):
user_answer = True
while keyboard.is_pressed("left"):
pass
break
elif keyboard.is_pressed('right'):
user_answer = False
while keyboard.is_pressed("right"):
pass
break
Another possible design is to use keyboard.on_press_key, which should only fire when the key changes state from "not pressed" to "pressed" (or when the auto repeat time elapses, which probably won't happen unless the user is doing it intentionally). You can abstract this out to a function to keep your addition_easy function clean:
import random
import keyboard
import time
def wait_for_keys(keys):
key_pressed = None
def key_press_event(key):
nonlocal key_pressed
key_pressed = key.name
for key in keys:
keyboard.on_press_key(key, key_press_event)
while key_pressed is None:
time.sleep(0.01)
return key_pressed
def addition_easy():
x = random.randint(1, 6)
y = random.randint(1, 6)
z = x + y
answer_correct = random.choice([True, False])
if answer_correct == False:
answer = (random.randint(2, 12))
else:
answer = z
if answer == z:
answer_correct = True
print(f"{x} + {y} = {answer}")
print("True or False?")
key = wait_for_keys(["left", "right"])
user_answer = (key == "left")
if user_answer == answer_correct:
return True
else:
return False
exercise_amount = 1000
for exercise in range(1, exercise_amount + 1):
addition_easy()
Not sure if you indented your function correctly. Try:
import random
import keyboard
def addition_easy():
x = random.randint(1, 6)
y = random.randint(1, 6)
z = x + y
answer_correct = random.choice([True, False])
if answer_correct == False:
answer = (random.randint(2, 12))
else:
answer = z
if answer == z:
answer_correct = True
print(f"{x} + {y} = {answer}")
print("True or False?")
while True:
if keyboard.is_pressed('left'):
user_answer = True
break
elif keyboard.is_pressed('right'):
user_answer = False
break
if user_answer == answer_correct:
return True
else:
return False
exercise_amount = int(input("How many exercises would you like to solve?"))
for exercise in range(1, exercise_amount + 1):
addition_easy()
Related
So I was going to make an arithmetic operator:
import random
solution = 0
summation = False
multiplication_table = False
subtraction = False
the_exercise = False
def arithmetic_operation(arithmetic_type):
stopper = []
exercise = input(
f"This is the {arithmetic_type} generator. Subjects: summation, multiplication table, subtraction.\nWhich of the subjects is "
"the student to practice? Please choose one of these 3 specifically")
if exercise == 'summation':
summation = True
elif exercise == 'multiplication table':
multiplication_table = True
elif exercise == 'subtraction':
subtraction = True
while summation == True:
ten_exercises = 0
if ten_exercises == 2:
summation == False
a = random.randint(0, 50)
b = random.randint(0, 50)
solution = a + b
print(f"What is {a} + {b}?")
the_exercise = True
return solution
while multiplication_table == True:
ten_exercises = 0
if ten_exercises == 2:
summation == False
a = random.randint(0, 10)
b = random.randint(0, 10)
solution = a * b
print(f"What is {a} * {b}?")
the_exercise = True
return solution
while subtraction == True:
ten_exercises = 0
if ten_exercises == 2:
summation == False
a = random.randint(21, 100)
b = random.randint(0, 20)
solution = a - b
print(f"What is {a} - {b}?")
the_exercise = True
return solution
arithmetic_operation("artithmetic exercise")
def main():
while the_exercise == True:
ans = input()
if int(ans) == solution:
print("Correct!")
stopper.append("STOP!")
if stopper.count("STOP!") == 10:
print("That's the end. Until next time!")
summation == False
else:
break
else:
print("Wrong! Try again")
ten_exercises += 1
if __name__ == '__main__':
main()
The problem is that my two functions aren't cooperating. According to the exercise, 'arithmetic_operation' is supposed to have a parameter.
What is it that I fail to understand? I have tried multiple solutions and replacing blocks of code to the main, but it doesn't work...
How can I restart the loop in the following code after it hits 1 since in 3N+1 it goes 1>4>2>1?
Code:
import math
import random
num = 1
NumTF = False
play = True
while play:
if num % 2 == 0:
num = num / 2
else:
num = 3 * num + 1
print(num)
if num == 1:
play = False
if play == False:
num += 1 and play == True
I'm assuming that you want to end the loop, because ...Collatz Conjecture.
All you have to do is add this simple if statement to the end:
elif num == 1:
play = False
after the if num%2 == 0 statement, so the while loop ends. Currently, your doing this after your doing num = 3*num+1, which makes it 4, so that case never happens. as rv.kvetch mentioned, the play==True does unexpected things, so just delete everything after print(num), as those are unnecessary.
I am writing code to a basic tictactoe game, but I am having trouble with a certain part of my code when trying to get it to break out of the while loop.
I have a list (= board) that contains 10 items = 'space'. The fullBoardCheck function should return a boolean = True when the condition is met, otherwise False.
Within initializeGame, the while loop is set to continue while gameStatus = False. However, once I run the game and fill out 9 spaces of the Board, gameStatus changes to True but it does not break out of the loop.
I cannot find what I am missing, and would appreciate any help you could offer.
Thanks
def placeMarkerO(board, position):
board[position] = 'O'
return board
def takeTurnO(board):
print("It's O's turn!")
wantedPosition = int(input('O, where would you like to go? (1-9): '))
while not spaceCheck(board, wantedPosition):
wantedPosition = int(input('This space is taken. Please choose an EMPTY space: '))
return wantedPosition
def fullBoardCheck(board):
# Returns true or false boolean checking if board is full
return len([x for x in board if x == " "]) == 1
def initializeGame():
board = [' '] * 10
gameStatus = False
while gameStatus == False:
drawBoard(board)
gameStatus = fullBoardCheck(board)
position = takeTurnX(board)
placeMarkerX(board, position)
drawBoard(board)
fullBoardCheck(board)
position = takeTurnO(board)
placeMarkerO(board, position)
You need to change the fullBoardCheck function:
def fullBoardCheck(board):
return len([x for x in board if x == ' ']) == 0
You checked if the len([x for x in board if x == ' ']) == 1, however, you need to check if the length is 0 because you want to return whether the board is completely full.
Good Luck!
I've just created my first little truth or dare/spin the bottle game for a senior/high school coding club on the Micro:bit. I would like to introduce using oop/classes/objects instead of (dreaded) global vars. The game works great on emulators such as https://create.withcode.uk/, but on the Micro:bit itself I encounter memory allocation errors as soon as I try to put pretty much anything into classes. Is the microbit's 16KB of RAM not enough? Or am I declaring classes incorrectly?
Coming from front-end and a bit of PHP/SQL, I'm a Python/memory knowledge noob so any help is appreciated.
If I use global vars in each of the functions it works without issue.
Here is the code:
from microbit import *
import random
#timer current function
#Global variables
class game:
gsTime = 3000
timerPrev = 0
numOfPlayers = 0
maxPlayers = 8
stage = 'start'
minSpinTime = 3000
class player:
#Which player is currently selected
selected = 0
#Player position display
def pos1(): display.set_pixel(2, 4, 9)
def pos2(): display.set_pixel(2, 0, 9)
def pos3(): display.set_pixel(0, 2, 9)
def pos4(): display.set_pixel(4, 2, 9)
def pos5(): display.set_pixel(0, 4, 9)
def pos6(): display.set_pixel(4, 0, 9)
def pos7(): display.set_pixel(0, 0, 9)
def pos8(): display.set_pixel(4, 4, 9)
#Array of all player positions
positions = [
[pos1, 1, Image.ARROW_S],
[pos2, 5, Image.ARROW_N],
[pos3, 3, Image.ARROW_W],
[pos4, 7, Image.ARROW_E],
[pos5, 2, Image.ARROW_SW],
[pos6, 6, Image.ARROW_NE],
[pos7, 4, Image.ARROW_NW],
[pos8, 8, Image.ARROW_SE]
]
positionsOrdered = []
class buttons:
pressed = False
class spinner:
completeSpins = 0
isCompleteSpin = False
rdTime = 0
stage = 'start'
stageStarted = False
gameCall = game()
playerCall = player()
buttonsCall = buttons()
spinnerCall = spinner()
#Return a random range of numbers
def rdRange(minMult,maxMult,base):
return random.randint(base*minMult, base*maxMult)
#return sort key of list
def getKey(item):
return item[1]
#Timer function
def timer(timeElapsed, onCompleteFunc):
if running_time() - gameCall.timerPrev >= timeElapsed:
onCompleteFunc()
#Position Players Start is true
def positionPlayersStartTrue():
game.stage = 'positionPlayers'
def selectNumOfPlayers(gteOrLte):
game.timerPrev = running_time()
if gteOrLte == 'gte':
if gameCall.numOfPlayers >= gameCall.maxPlayers:
game.numOfPlayers = 1
else:
game.numOfPlayers += 1
else:
if gameCall.numOfPlayers <= 1:
game.numOfPlayers = maxPlayers
else:
game.numOfPlayers -= 1
display.show(str(gameCall.numOfPlayers)) #Have to convert int to string before passing to display.show func
buttons.pressed = True
#Ask for number of players up to maxPlayers.
def setPlayerNum():
#If B is pressed increment by 1 up the max players and cycle back to 1
if button_b.was_pressed():
selectNumOfPlayers('gte')
#If A is pressed decrement by 1 down to 1 and then cycle back to maxPlayers var
elif button_a.was_pressed():
selectNumOfPlayers('lte')
elif buttonsCall.pressed == False:
#Ask how many players
display.show('#?')
else:
timer(gameCall.gsTime, positionPlayersStartTrue)
#display the position of players
def positionPlayers():
buttons.pressed = False
display.clear()
for i in range(gameCall.numOfPlayers):
el = player.positions[i]
player.positionsOrdered.append(el)
el[0]()
player.positionsOrdered.sort(key=getKey)
while buttonsCall.pressed == False:
startSpin()
#start the spin - useful for starting the spin after one spin was complete too
def startSpin():
if button_a.was_pressed() or button_b.was_pressed():
buttons.pressed = True
game.stage = 'spin'
#Spin start
def spin():
if spinnerCall.stage == 'start' and spinnerCall.stageStarted == False:
game.timerPrev = running_time()
spinner.rdTime = rdRange(200, 700, gameCall.numOfPlayers)
spinner.stageStarted = True
print(spinner.rdTime)
for i in range(gameCall.numOfPlayers):
display.clear()
el = player.positionsOrdered[i]
el[0]()
if i + 1 == gameCall.numOfPlayers:
spinner.completeSpins += 1
spinner.isCompleteSpin = True
else:
spinner.isCompleteSpin = False
if spinnerCall.stage == 'start':
if (running_time() - gameCall.timerPrev >= (gameCall.minSpinTime + spinnerCall.rdTime)) and (spinnerCall.isCompleteSpin == True):
spinner.stage = 'slow'
spinner.stageStarted = False
sleep(200)
#Slower spin to emulate spinner slowing down as it comes near to stopping. Should probably use some clever-er maths here instead.
elif spinner.stage == 'slow':
if spinnerCall.stageStarted == False:
game.timerPrev = running_time()
spinner.rdTime = rdRange(500, 900, gameCall.numOfPlayers)
spinner.stageStarted = True
print(spinnerCall.rdTime)
if running_time() - gameCall.timerPrev >= spinnerCall.rdTime:
spinner.stage = 'stop'
spinner.stageStarted = False
sleep(400)
elif spinner.stage == 'stop':
player.selected = i
game.stage = 'selectedPlayer'
# reset spinner stage for next spin
spinner.stage = 'start'
break
#Player has been selected
def selectedPlayer():
el = playerCall.positionsOrdered[playerCall.selected]
sleep(200)
display.show(el[2])
sleep(200)
display.clear()
while True:
#CALL FUNCTIONS
if gameCall.stage == 'start':
setPlayerNum()
elif gameCall.stage == 'positionPlayers' and buttonsCall.pressed == True:
positionPlayers()
elif gameCall.stage == 'spin':
spin()
elif gameCall.stage == 'selectedPlayer':
#print('this one is selected ', playerCall.selected)
selectedPlayer()
#start spin again if button is pressed
startSpin()
Your code is too big for microbit. Microbit is limited by 16KB of RAM. To decrease size of your code you can:
minify it directly from Mu editor or use any other minifier lib
Shrink variable names
Delete comments
So for a school project we need to make Tic Tac Toe in Python. This is currently where i'm at. It semi works, yet without win detection. It is very long winded, and I am trying to condense all the if statements into loops, but I dont really know how to get it into a nice loop. Each if statement within the main function is ran depending on the input, which results in different items in the 2D list (board) being changed.
row1 = ["-","-","-"]
row2 = ["-","-","-"]
row3 = ["-","-","-"]
board = [row1, row2, row3]
rows = 3
for each in range(0,3):
print(*board[each])
A1 = board[0][0]
A2 = board[0][1]
A3 = board[0][2]
B1 = board[1][0]
B2 = board[1][1]
B3 = board[1][2]
C1 = board[2][0]
C2 = board[2][1]
C3 = board[2][2]
X = True
game = True
def main(game, X):
while game == True:
if X == True:
value = input("Position in Grid for X: ")
if X == False:
value = input("Position in Grid for O: ")
if value == "A1":
if X == True:
board[0][0]="X"
X = False
elif X == False:
board[0][0]="O"
X = True
for each in range(0,3):
print(*board[each])
game = True
if value == "A2":
if X == True:
board[0][1]="X"
X = False
elif X == False:
board[0][1]="O"
X = True
for each in range(0,3):
print(*board[each])
game = True
if value == "A3":
if X == True:
board[0][2]="X"
X = False
elif X == False:
board[0][2]="O"
X = True
for each in range(0,3):
print(*board[each])
game = True
if value == "B1":
if X == True:
board[1][0]="X"
X = False
elif X == False:
board[1][0]="O"
X = True
for each in range(0,3):
print(*board[each])
game = True
if value == "B2":
if X == True:
board[1][1]="X"
X = False
elif X == False:
board[1][1]="O"
X = True
for each in range(0,3):
print(*board[each])
game = True
if value == "B3":
if X == True:
board[1][2]="X"
X = False
elif X == False:
board[1][2]="O"
X = True
for each in range(0,3):
print(*board[each])
game = True
if value == "C1":
if X == True:
board[2][0]="X"
X = False
elif X == False:
board[2][0]="O"
X = True
for each in range(0,3):
print(*board[each])
game = True
if value == "C2":
if X == True:
board[2][1]="X"
X = False
elif X == False:
board[2][1]="O"
X = True
for each in range(0,3):
print(*board[each])
game = True
if value == "C3":
if X == True:
board[2][2]="X"
X = False
elif X == False:
board[2][2]="O"
X = True
for each in range(0,3):
print(*board[each])
game = True
Use functions, at least one that converts an input to a tuple of indices (row and column).
This function should split the input into two characters. The letter can then be converted to the row, the digit to column. These conversions can be done independently and with a few tricks (using ord() on the letter and int() on the digit plus some math) very short. The function then returns an index tuple to use on board.
One way to shorten this code is to get a method to turn a string, like "B3" into the corresponding indices of the 2D array, [1][2].
The method can look like this:
def indices_of_string(s):
letters = ["A", "B", "C"]
numbers = ["1", "2", "3"]
return (letters.index(s[0]), numbers.index(s[1]))
Then you can use it like this:
(x, y) = indices_of_string(value)
if X == True:
board[x][y]="X"
X = False
elif X == False:
board[x][y]="O"
X = True
for each in range(0,3):
print(*board[each])
game = True
Your control loops aren't properly indented beneath while game == True right at the start of your main method. Try fixing that to see if the game works then?
EDIT:
If you want to shorten, you could try using a dictionary to map your grid like follows. I modified your code a bit. As for win conditions etc., you should be able to add further conditionals for those.
X = True
game = True
def main(game, X):
#Initialize the gameboard.
row1 = ["-","-","-"]
row2 = ["-","-","-"]
row3 = ["-","-","-"]
board = [row1, row2, row3]
for each in range(0,3):
print(*board[each])
table = {'A1': board[0][0], 'A2': board[0][1], 'A3': board[0][2],
'B1': board[1][0], 'B2': board[1][1], 'B3': board[1][2],
'C1': board[2][0], 'C2': board[2][1], 'C3': board[2][2]}
while game == True:
if X == True:
value = input("Position in Grid for X: ")
if X == False:
value = input("Position in Grid for O: ")
if value in table.keys():
if X == True:
table[value]="X"
X = False
elif X == False:
table[value]="O"
X = True
rows = list(table.values())
board = [rows[0:3],rows[3:6],rows[6:9]]
for each in range(0,3):
print(*board[each])
game = True
There are still problems with overwriting positions etc., but you only have one main if elif loop to worry about now when generating player moves. Good luck!
You can shorten your code by applying the DRY principle. In this case not only are you using a lot of if statements, they're also all very similar. If you analyse what the differences are, you can often generalize what going on by adding or computing variables that can then be used in place of hardcoded literal values like "A1" and [0][1].
I've partially done this to your code, and ended-up with the following. You could easily take it a little further by create a display_board() function and calling it instead of repeating the for each in range(0, 3): loop in more than one place.
row1 = ["-","-","-"]
row2 = ["-","-","-"]
row3 = ["-","-","-"]
board = [row1, row2, row3]
rows = 3
for each in range(0, 3):
print(*board[each])
X = True
game = True
def main(game, X):
while game == True:
if X == True:
value = input("Position in Grid for X: ")
if X == False:
value = input("Position in Grid for O: ")
## Convert two-letter value string to integer column and row indices.
col, row = ord(value[0])-ord("A"), int(value[1])-1
if X == True:
board[col][row]="X"
X = False
elif X == False:
board[col][row]="O"
X = True
for each in range(0, 3):
print(*board[each])
main(game, X)
Note Both this and your code are infinite-loops because nothing ever sets game = False. I assume you'll eventually add something that checks and determines if the game is over and which player, if any, won.