I'm doing an interactive map and after reading on the Internet, I'm trying to develop my learn towards an efficient way of programming. I wonder, is there a way to write more efficient this kinds of if_elif tree's ? I've read that one way is to make every conditions a def and store the conditions in a tuple ( 'cause is more efficiente than a list, and the values won't change). I've considered using ternary operator but don't know if is gonna be more efficient.
if direction == "w":
point[POS_Y] -= 1
elif direction == "s":
point[POS_Y] += 1
elif direction == "a":
point[POS_X] -= 1
elif direction == "d":
point[POS_X] += 1
Here's the code:
POS_X = 0
POS_Y = 1
MAP_WIDTH = 20
MAP_HEIGHT = 15
point = [3,7]
while 1: # i've read that Python is more efficient using a 1 instead of a True
# def player_movement(point):
print('+' + '-' * MAP_WIDTH * 3 + '+')
for coordinate_y in range(MAP_HEIGHT):
print('|',end='')
for coordinate_x in range(MAP_WIDTH):
if point[POS_X] == coordinate_x and point[POS_Y] == coordinate_y:
print(' #',end='') # it works with ' # ' as well, but in Visual Code doesn't see though
else:
print(' ',end='')
print('|')
print('+' + '-' * MAP_WIDTH * 3 + '+')
# player_movement([3,8])
direction = readchar.readchar().decode()
if direction == "w":
point[POS_Y] -= 1
elif direction == "s":
point[POS_Y] += 1
elif direction == "a":
point[POS_X] -= 1
elif direction == "d":
point[POS_X] += 1
If you have python 3.10 and above you can use the switch statement.
Here is an example:
status = 404
def http_error(status):
match status:
case 400:
return "Bad request"
case 404:
return "Not found"
case 418:
return "I'm a teapot"
case _:
return "Something's wrong with the internet"
This is called structural pattern matching and the link to the docs are here: https://docs.python.org/3.10/whatsnew/3.10.html#pep-634-structural-pattern-matching
As mentioned in the comments, this is no faster that if, elif statements from previous versions. So is important to mention.
After doing a quick test (python version 3.10.0) it seems that elif takes the most time, match comes second and using a dict is the fastest way.
test_elif
def test_elif():
direction = random.choice(['w','a','s','d'])
if direction == "w":
point[1] -= 1
elif direction == "s":
point[1] += 1
elif direction == "a":
point[0] -= 1
elif direction == "d":
point[0] += 1
return
test_match
def test_match():
direction = random.choice(['w','a','s','d'])
match direction:
case "w":
point[1] -= 1
case "s":
point[1] += 1
case "a":
point[0] -= 1
case "d":
point[0] += 1
return
test_dict
def test_dict():
direction = random.choice(['w','a','s','d'])
md = modifiers[direction]
point[md[0]] += md[1]
return
setup
import random
modifiers = {
"w": (1, -1),
"s": (1, +1),
"a": (0, -1),
"d": (0, +1)
}
point = [0,0]
Tested with timeit.timeit(funcntion_call, setup=setup, number=1000000)
test_elif: 0.1586214000126347 seconds
test_match: 0.1482315000030212 seconds
test_dict: 0.13923519995296374 seconds
Related
The program should ask what its first coordinates are. Then movement is asked and its answer should be with
r,R,l,L,u,U,d,D.
Coordinate are input with a comma (x,y), and then again if 'u','d','l','r' are input then the x or y axis will increase or decrease.
'u' and 'd' for the y-axis; 'r' and 'l' for the x-axis.
def program_for_grid(x,y,where):
if where == 'r' or where == 'R':
# move x upp +
for r in range(len(where)):
r == 'r' or r == 'R'
x + 1
elif where == 'l' or where == 'L':
# move x down -
for l in range(len(where)):
l == 'l' or l == 'L'
x - 1
elif where == 'u' or where == 'U':
# move y up +
for u in range(len(where)):
u == 'u' or u == 'U'
y + 1
elif where == 'd' or where == 'D':
# move y down -
for d in range(len(where)):
d == 'd' or d == 'D'
y - 1
x_axis,y_axis = input('Initial position: ').split(',')
int_x_axix = int(x_axis)
int_y_axix = int(x_axis)
movement = input('Movement: ')
grid = program_for_grid(int_x_axix,int_y_axix,movement)
print(grid)
Something like this?
def delta(movement):
"""
Parse the string for movement instructions and
return resulting relative coordinates
"""
x = 0
y = 0
for letter in movement.lower():
if letter == "d": y -= 1
elif letter == "u": y += 1
elif letter == "l": x -= 1
elif letter == "r": x += 1
else:
print("warning: skipping undefined", letter)
return x, y
x_axis,y_axis = input('Initial position: ').split(',')
int_x_axix = int(x_axis)
int_y_axix = int(y_axis)
while True:
movement = input('Movement: ')
xdelta, ydelta = delta(movement)
int_x_axix += xdelta
int_y_axix += ydelta
print(f"resulting position: ({int_x_axix},{int_y_axix})")
How to make it so the inserted image moves to a specific location and ends the program?
I tried using a counter loop but I still can't figure it out.
mr_krabs2.draw(win3)
key = win3.getKey()
counter = 0
while True:
counter +=1
if key == "w":
mr_krabs2.move(0,-10)
counter = count - 1
time.sleep(0.1)
if key == "s":
mr_krabs2.move(0,10)
time.sleep(0.1)
if key == "a":
mr_krabs2.move(-10,0)
time.sleep(0.1)
if key == "d":
mr_krabs2.move(10,0)
time.sleep(0.1)
for count in range (10):
print("a")
time.sleep(10)
win3.close
I solved my question.
# Variables for x and y to have a precise location.
x = 0.0
y = 0.0
while True:
key = win3.getKey()
# Key Movement.
if key == "w":
mr_krabs2.move(0,-10)
y = y + 10
print("\nx =",x,"y =",y,"\n")
if key == "s":
mr_krabs2.move(0,10)
y = y - 10
print("\nx =",x,"y =",y,"\n")
if key == "a":
mr_krabs2.move(-10,0)
x = x + 10
print("\nx =",x,"y =",y,"\n")
if key == "d":
mr_krabs2.move(10,0)
x = x - 10
print("\nx =",x,"y =",y,"\n")
# Making a hitbox
if (x < (59) and
x > (52) and
y < (4) and
y > (-6)):
time.sleep(5)
win3.close()
break
The while loop only runs once and exits as though the condition has been met, however, when debugging for whether the condition has been met externally (after the loop) it has not.
Here is my code:
iFile = open("snakein.txt")
line = iFile.readline()
finXStr,finYStr = line.split()
finX = int(finXStr)
finY = int(finYStr)
moves = []
snkX = 0
snkY = 0
while snkX != finX and snkY != finY:
if finX > 0 and finX != snkX:
moves.append("R")
snkX += 1
print("x moves")
elif finX < 0 and finX != snkX:
moves.append("L")
snkX -= 1
print("x moves")
elif finX == snkX:
moves.append("L")
snkX -= 1
print("x moves")
if finY < 0 and moves[-1] == "R":
moves.append("R")
snkY -= 1
print("y moves")
elif finY < 0 and moves[-1] == "L":
moves.append("L")
snkY -= 1
print("y moves")
elif finY > 0 and moves[-1] == "R":
moves.append("L")
snkY += 1
print("y moves")
elif finY > 0 and moves[-1] == "L":
moves.append("R")
snkY += 1
print("y moves")
elif finY == snkY:
moves.appebd("L")
snakeY -= 1
print("y moves")
output = moves
oFile = open("snakeout.txt", "w")
oFile.write(str(output))
The program is trying to make a "snake" move in the most efficient way to a specific target via certain moves, on a "cartesian plane" like setting.
It looks like while snkX != finX or snkY != finY: might make more sense in your context...
I'm building out Battleships game in Python. I have a list and I'm trying to build a validation tool in Python to catch user inputs that are outside the 10x10 range of my list.
Here is the code:
from random import randint
player = "User"
board = []
board_size = 10
ships = {"Aircraft Carrier":5,
"Battleship":4,
"Submarine":3,
"Destroyer":3,
"Patrol Boat":2}
def print_board(player, board): # to print joined board
print("Here is " + player + "'s board")
for row in board:
print(" ".join(row))
def switch_user(player): # to switch users
if player == "User":
player = "Computer"
elif player == "Computer":
player = "User"
else:
print("Error with user switching")
for x in range(0, board_size): # to create a board
board.append(["O"] * board_size)
print_board(player,board)
def random_row(board): # generate random row
return randint(0, len(board) - 1)
def random_col(board): # generate random column
return randint(0, len(board[0]) - 1)
def user_places_ships(board, ships): # user choses to place its ships by providing starting co-ordinate and direction.
for ship in ships:
valid = False
while(not valid):
user_input_coordinates = input("Please enter row & column number for your " + str(ship) + ", which is " + str(ships[ship]) + "-cells long (row, column).")
ship_row, ship_col = user_input_coordinates.split(",")
ship_row = int(ship_row)
ship_col = int(ship_col)
user_input_dir = input("Please enter direction for your " + str(ship) + ", which is " + str(ships[ship]) + "-cells long (h for horizontal or v for vertical).")
valid = validate_coordinates(board, ships[ship], ship_row, ship_col, user_input_dir)
if not valid:
print("The ship coordinates either outside of" , board_size, "X" , board_size, "range, overlap with or too close to another ship.")
place_ship(board, ships[ship], ship_row, ship_col, user_input_dir)
print("You have finished placing all your ships.")
def validate_coordinates(board, ship_len, row, col, dir): # validates if the co-ordinates entered by a player are within the board and don't overlap with other ships
if dir == "h" or dir == "H":
for x in range(ship_len):
if row-1 > board_size or col-1+x > board_size:
return False
elif row-1 < 0 or col-1+x < 0:
return False
elif board[row-1][col-1+x] == "S":
return False
elif dir == "v" or dir == "V":
for x in range(ship_len):
if row-1+x > board_size or col-1 > board_size:
return False
elif row-1+x < 0 or col-1 < 0:
return False
elif board[row-1+x][col-1] == "S":
return False
return True
def place_ship(board, ship_len, row, col, dir): # to actually place ships and mark them as "S"
if dir == "h" or dir == "H":
for x in range(ship_len):
board[row-1][col-1+x] = "S"
elif dir == "v" or dir == "V":
for x in range(ship_len):
board[row-1+x][col-1] = "S"
else:
print("Error with direction.")
print_board(player,board)
user_places_ships(board,ships)
If a user enters "10,10" for ship coordinates and "h" for horizontal direction, then Python generates the following error message:
Traceback (most recent call last): File "C:/Users/Elchin's PC/Downloads/battleships game.py", line 85, in <module>
user_places_ships(board,ships) File "C:/Users/Elchin's PC/Downloads/battleships game.py", line 49, in user_places_ships
valid = validate_coordinates(board, ships[ship], ship_row, ship_col, user_input_dir) File "C:/Users/Elchin's PC/Downloads/battleships game.py", line 62, in validate_coordinates
elif board[row-1][col-1+x] == "S": IndexError: list index out of range
I know that the error is in this line:
elif board[row-1][col-1+x] == "S":
return False
But I don't know how to fix it. Could you please help me figure out the solution?
If a list has length n, you can access indices from 0 to n-1 (both inclusive).
Your if statements however check:
if row-1+x > board_size or col-1 > board_size: # greater than n
return False
elif row-1+x < 0 or col-1 < 0: # less than 0
return False
elif board[row-1+x][col-1] == "S":
return False
So as a result, if we reach the last elif part, we have guarantees that the indices are 0 < i <= n. But these should be 0 < i < n.
So you should change the first if statement to:
if row-1+x >= board_size or col-1 >= board_size: # greater than or equal n
return False
elif row-1+x < 0 or col-1 < 0: # less than 0
return False
elif board[row-1+x][col-1] == "S":
return False
You can make the code more elegant by writing:
if not (0 < row-1+x < board_size and 0 < col-1 < board_size) or \
board[row-1+x][col-1] == "S":
return False
I edited my previous question because I came up with the code I think is correct.
The logic behind this should be:
while the set is not over and it's not a tie 10:10: player A starts serving and does it twice regardless he wins points or not, then player B takes serve and does it twice also. It continues until the set is over, except there is a tie 10:10 when servers change each point scored.
Can anyone check if the code is flawless? thank you.
def simOneSet(probA, probB):
serving = "A"
scoreA = scoreB = 0
while not setOver(scoreA, scoreB):
if scoreA != 10 and scoreB != 10:
if serving == "A":
for i in range(2):
if random() < probA:
scoreA += 1
else:
scoreB += 1
serving = "B"
else:
for i in range(2):
if random() < probB:
scoreB +=1
else:
scoreA += 1
serving = "A"
# when there is a tie 10:10
else:
if serving == "A":
if random() < probA:
scoreA += 1
serving = "B"
else:
scoreB += 1
serving = "B"
else:
if random() < probB:
scoreB += 1
serving = "B"
else:
scoreA += 1
serving = "A"
return scoreA, scoreB
I would use a dict to "switch" between players:
other = {'A':'B', 'B':'A'}
Then, if serving equals 'A', then other[serving] would equal 'B', and if serving equals 'B', then other[serving] would equal 'A'.
You could also use a collections.Counter to keep track of the score:
In [1]: import collections
In [2]: score = collections.Counter()
In [3]: score['A'] += 1
In [4]: score['A'] += 1
In [5]: score['B'] += 1
In [6]: score
Out[6]: Counter({'A': 2, 'B': 1})
Also notice how in this piece of code
if serving == "A":
for i in range(2):
if random() < probA:
scoreA += 1
else:
scoreB += 1
else:
for i in range(2):
if random() < probB:
scoreB +=1
else:
scoreA += 1
there are two blocks which are basically the same idea repeated twice. That's a sign that the code can be tightened-up by using a function. For example, we could define a function serve which when given a probability prob and a player (A or B) returns the player who wins:
def serve(prob, player):
if random.random() < prob:
return player
else:
return other[player]
then the above code would become
for i in range(2):
winner = serve(prob[serving], serving)
score[winner] += 1
Thus, you can compactify your code quite a bit this way:
import random
import collections
other = {'A':'B', 'B':'A'}
def serve(prob, player):
if random.random() < prob:
return player
else:
return other[player]
def simOneSet(probA, probB):
prob = {'A':probA, 'B':probB}
score = collections.Counter()
serving = "A"
while not setOver(score['A'], score['B']):
for i in range(2):
winner = serve(prob[serving], serving)
score[winner] += 1
if score['A'] == 10 and score['B'] == 10:
winner = serve(prob[serving], serving)
score[winner] += 1
serving = winner
return score['A'], score['B']
def setOver(scoreA, scoreB):
return max(scoreA, scoreB) >= 21
print(simOneSet(0.5,0.5))
Here is a hint:
If you have the roundnumber in the set and know which player started serving, you have everything you need to know who is serving.
Then a simple if statement at the start or end of your loop should be enough.
If this is too complicated, try starting by simulating a game where the server starts every round.
Something that might help is the syntax
var = 1 if var == 2 else 2
Which will make var be 1 if var is 2, and var be 2 if var is 1. I feel as though this is a school problem, so I don't want to totally give away the answer :)
Hint: You're on the right track with your thinking.
from random import *
P1=P2=0
while 1 :
p1=p2=0
while 2 :
if random() < 0.5 : p1 +=1
else : p2 +=1
if(p1 >=11 or p2 >=11) and abs(p1-p2) > 1: break
P1 += p1 > p2; P2 += p2 > p1
print "%2d : %2d (%d : %d)" % (p1, p2, P1, P2)
if P1 == 4 or P2 == 4 : break
i hope this helps, it worked for me