Ok. This article is for all the people, who have written code like this one:
1 2 3 4 |
if direction == "left" then if column > 0 then: new_row = current_row new_col = current_col - 1 |
When they were solving a problem like this one:
You know who you are. You are probably thinking what is wrong with it? After all, 4 directions, it is quite ok to put an if for each one. Sometimes the directions are 8, as the diagonals are taken into account, but still, we are talking about 8 conditions. Not such a pain…
Well, although the above is true, and 8 if-s (or even 16 if something has to be checked additionally) are not such a pain, but there is a better way to do it. And it is in making a dictionary of lists with the moves, that are needed to be taken into account, when the direction is “left”. Something like this:
1 2 3 4 5 6 |
dirs = { "up":[-1,0], "down":[1,0], "left":[0,-1], "right":[0,1] } |
Once you have this dictionary and you need to move 5 ( number is the variable, holding the 5) to the left from the current position in the matrix, it looks beautiful:
1 2 3 4 |
direction_change = [number * n for n in dirs[direction]] row_to_shoot = int(player_pos[0]) + direction_change[0] col_to_shoot = int(player_pos[1]) + direction_change[1] next_pos = [row_to_shoot, col_to_shoot] |
Of course, you still need to make the check for the moving, but it looks quite ok as a beautiful 1-liner:
if n > row_to_shoot >= 0 and 0 <= col_to_shoot < n:
At the end if you thinking, that the code above is from some kind of a program I have written today – you are right. This is how the problem looks like:
Write a program that receives a state of a battlefield, reads some attack commands and changes the state of the field. On the first line you will be given the size of the field (N). On the next N lines, you will be given the state of the field represented as some symbols separated by single space.
Here are the possible symbols and their meaning:
- “.” – empty field
- “p” – the starting position of the plane
- “t” – a target that the plane wants to destroy
After the field state you will be given another number (M). On the next M lines, you will be given some commands:
- move {right/left/up/down} {steps} – the plane moves in the given direction with the given steps. Move the player only if the filed he wants to step on is marked with “.”
- shoot {right/left/up/down} {steps} – the plane shoots in the given direction with the given steps (from his current position without moving), the field gets destroyed and marked with a “x”. If the plane shoots at a target, it also gets destroyed. When a field is destroyed, the plane can no longer step on it.
- Validate the positions, since they can be outside the field
Keep track of all of the killed targets. If at any point there are no targets left, end the program and print “Mission accomplished! All {count_targets} targets destroyed.”. If after all the commands, there are still targets left print “Mission failed! {count_left_targets} targets left.”.
Finally, print the state of the field as shown in the examples
This is what I managed to do, while I was in a hurry solving it:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 |
def print_victory(): if targets_count == 0: print(f'Mission accomplished! All {initial_targets} targets destroyed.') else: print(f'Mission failed! {targets_count} targets left.') [print(" ".join(row)) for row in field] exit() n = int(input()) field = [] dirs = { "up":[-1,0], "down":[1,0], "left":[0,-1], "right":[0,1] } targets_count = 0 for row in range(n): input_line = input() line = list(input_line.split()) field.append(line) if 'p' in line: player_pos = [row, line.index("p")] targets_count += input_line.count('t') commands_count = int(input()) initial_targets = targets_count for _ in range(commands_count): if targets_count == 0: print_victory() command = input().split() action = command[0] #shoot direction = command[1] #down number = int(command[2]) #2 direction_change = [number * n for n in dirs[direction]] row_to_shoot = int(player_pos[0]) + direction_change[0] col_to_shoot = int(player_pos[1]) + direction_change[1] next_pos = [row_to_shoot, col_to_shoot] if n > row_to_shoot >= 0 and 0 <= col_to_shoot < n: if action == "shoot": if field[row_to_shoot][col_to_shoot] == 't': targets_count -= 1 field[row_to_shoot][col_to_shoot] = "x" elif action == "move": if field[row_to_shoot][col_to_shoot] == '.': field[row_to_shoot][col_to_shoot] = "p" field[player_pos[0]][player_pos[1]] = '.' player_pos = next_pos # [print(" ".join(row)) for row in field] # print("------------------------------------") print_victory() |
Not my best piece of code, but I was ok as far as it worked.