From cf55a5f63756691ce3145636cbaf43606fc61831 Mon Sep 17 00:00:00 2001 From: Bernard Cooke Date: Fri, 14 Jan 2022 01:28:30 +0000 Subject: [PATCH 1/4] 12. Port Bombs Away to Python --- 12_Bombs_Away/python/bombs_away.py | 177 +++++++++++++++++++++++++++++ 1 file changed, 177 insertions(+) create mode 100644 12_Bombs_Away/python/bombs_away.py diff --git a/12_Bombs_Away/python/bombs_away.py b/12_Bombs_Away/python/bombs_away.py new file mode 100644 index 00000000..894f968c --- /dev/null +++ b/12_Bombs_Away/python/bombs_away.py @@ -0,0 +1,177 @@ +""" +Bombs away + +Ported from BASIC to Python3 by Bernard Cooke (bernardcooke53) +Tested with Python 3.8.10, formatted with Black and type checked with mypy. +""" +import random +from typing import Iterable + + +def _stdin_choice(*, prompt: str, choices: Iterable[str]) -> str: + ret = input(prompt) + while ret not in choices: + print("TRY AGAIN...") + ret = input(prompt) + return ret + + +def player_survived() -> None: + print("YOU MADE IT THROUGH TREMENDOUS FLAK!!") + + +def player_death() -> None: + print("* * * * BOOM * * * *") + print("YOU HAVE BEEN SHOT DOWN.....") + print("DEARLY BELOVED, WE ARE GATHERED HERE TODAY TO PAY OUR") + print("LAST TRIBUTE...") + + +def mission_success() -> None: + print(f"DIRECT HIT!!!! {int(100 * random.random())} KILLED.") + print("MISSION SUCCESSFUL.") + + +def death_with_chance(p_death: float) -> bool: + """ + Takes a float between 0 and 1 and returns a boolean + if the player has survived (based on random chance) + + Returns True if death, False if survived + """ + return p_death > random.random() + + +def commence_non_kamikazi_attack() -> None: + nmissions = int(input("HOW MANY MISSIONS HAVE YOU FLOWN? ")) + + while nmissions >= 160: + print("MISSIONS, NOT MILES...") + print("150 MISSIONS IS HIGH EVEN FOR OLD-TIMERS") + nmissions = int(input("NOW THEN, HOW MANY MISSIONS HAVE YOU FLOWN? ")) + + if nmissions >= 100: + print("THAT'S PUSHING THE ODDS!") + + if nmissions < 25: + print("FRESH OUT OF TRAINING, EH?") + + print() + return ( + mission_success() if nmissions >= 160 * random.random() else mission_failure() + ) + + +def mission_failure() -> None: + weapons_choices = { + "1": "GUNS", + "2": "MISSILES", + "3": "BOTH", + } + print(f"MISSED TARGET BY {int(2 + 30 * random.random())} MILES!") + print("NOW YOU'RE REALLY IN FOR IT !!") + print() + enemy_weapons = _stdin_choice( + prompt="DOES THE ENEMY HAVE GUNS(1), MISSILES(2), OR BOTH(3)? ", + choices=weapons_choices.keys(), + ) + + # If there are no gunners (i.e. weapon choice 2) then + # we say that the gunners have 0 accuracy for the purposes + # of calculating probability of player death + + enemy_gunner_accuracy = 0.0 + if enemy_weapons != "2": + # If the enemy has guns, how accurate are the gunners? + enemy_gunner_accuracy = float( + input("WHAT'S THE PERCENT HIT RATE OF ENEMY GUNNERS (10 TO 50)? ") + ) + + if enemy_gunner_accuracy < 10: + print("YOU LIE, BUT YOU'LL PAY...") + return player_death() + + missile_threat_weighting = 0 if enemy_weapons == "1" else 35 + + death = death_with_chance( + p_death=(enemy_gunner_accuracy + missile_threat_weighting) / 100 + ) + + return player_survived() if not death else player_death() + + +def play_italy() -> None: + targets_to_messages = { + # 1 - ALBANIA, 2 - GREECE, 3 - NORTH AFRICA + "1": "SHOULD BE EASY -- YOU'RE FLYING A NAZI-MADE PLANE.", + "2": "BE CAREFUL!!!", + "3": "YOU'RE GOING FOR THE OIL, EH?", + } + target = _stdin_choice( + prompt="YOUR TARGET -- ALBANIA(1), GREECE(2), NORTH AFRICA(3)", + choices=targets_to_messages.keys(), + ) + + print(targets_to_messages[target]) + return commence_non_kamikazi_attack() + + +def play_allies() -> None: + aircraft_to_message = { + "1": "YOU'VE GOT 2 TONS OF BOMBS FLYING FOR PLOESTI.", + "2": "YOU'RE DUMPING THE A-BOMB ON HIROSHIMA.", + "3": "YOU'RE CHASING THE BISMARK IN THE NORTH SEA.", + "4": "YOU'RE BUSTING A GERMAN HEAVY WATER PLANT IN THE RUHR.", + } + aircraft = _stdin_choice( + prompt="AIRCRAFT -- LIBERATOR(1), B-29(2), B-17(3), LANCASTER(4): ", + choices=aircraft_to_message.keys(), + ) + + print(aircraft_to_message[aircraft]) + return commence_non_kamikazi_attack() + + +def play_japan() -> None: + print("YOU'RE FLYING A KAMIKAZE MISSION OVER THE USS LEXINGTON.") + first_mission = input("YOUR FIRST KAMIKAZE MISSION? (Y OR N): ") + if first_mission.lower() == "n": + return player_death() + + if random.random() > 0.65: + return mission_success() + return player_death() + + +def play_germany() -> None: + targets_to_messages = { + # 1 - RUSSIA, 2 - ENGLAND, 3 - FRANCE + "1": "YOU'RE NEARING STALINGRAD.", + "2": "NEARING LONDON. BE CAREFUL, THEY'VE GOT RADAR.", + "3": "NEARING VERSAILLES. DUCK SOUP. THEY'RE NEARLY DEFENSELESS.", + } + target = _stdin_choice( + prompt="A NAZI, EH? OH WELL. ARE YOU GOING FOR RUSSIA(1),\nENGLAND(2), OR FRANCE(3)? ", + choices=targets_to_messages.keys(), + ) + + print(targets_to_messages[target]) + + return commence_non_kamikazi_attack() + + +def play_game() -> None: + print("YOU ARE A PILOT IN A WORLD WAR II BOMBER.") + sides = {"1": play_italy, "2": play_allies, "3": play_japan, "4": play_germany} + side = _stdin_choice( + prompt="WHAT SIDE -- ITALY(1), ALLIES(2), JAPAN(3), GERMANY(4): ", + choices=sides.keys(), + ) + return sides[side]() + + +if __name__ == "__main__": + again = True + while again: + play_game() + again = True if input("ANOTHER MISSION (Y OR N)").upper() == "Y" else False From 11ffe9cf90d7b7f61c7fe576b1ad7b61f451daee Mon Sep 17 00:00:00 2001 From: Bernard Cooke Date: Fri, 14 Jan 2022 01:31:27 +0000 Subject: [PATCH 2/4] Tidy up 'Another mission?' message --- 12_Bombs_Away/python/bombs_away.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/12_Bombs_Away/python/bombs_away.py b/12_Bombs_Away/python/bombs_away.py index 894f968c..a7c4ccd5 100644 --- a/12_Bombs_Away/python/bombs_away.py +++ b/12_Bombs_Away/python/bombs_away.py @@ -174,4 +174,4 @@ if __name__ == "__main__": again = True while again: play_game() - again = True if input("ANOTHER MISSION (Y OR N)").upper() == "Y" else False + again = True if input("ANOTHER MISSION? (Y OR N): ").upper() == "Y" else False From f29fa1792ce2dc0b7f161ae631a3cd63854cdf3f Mon Sep 17 00:00:00 2001 From: Bernard Cooke Date: Fri, 14 Jan 2022 01:53:41 +0000 Subject: [PATCH 3/4] Tidy logic with inline conditionals for clarity --- 12_Bombs_Away/python/bombs_away.py | 52 +++++++++++++++++++----------- 1 file changed, 33 insertions(+), 19 deletions(-) diff --git a/12_Bombs_Away/python/bombs_away.py b/12_Bombs_Away/python/bombs_away.py index a7c4ccd5..b3133ae3 100644 --- a/12_Bombs_Away/python/bombs_away.py +++ b/12_Bombs_Away/python/bombs_away.py @@ -8,7 +8,7 @@ import random from typing import Iterable -def _stdin_choice(*, prompt: str, choices: Iterable[str]) -> str: +def _stdin_choice(prompt: str, *, choices: Iterable[str]) -> str: ret = input(prompt) while ret not in choices: print("TRY AGAIN...") @@ -43,12 +43,22 @@ def death_with_chance(p_death: float) -> bool: def commence_non_kamikazi_attack() -> None: - nmissions = int(input("HOW MANY MISSIONS HAVE YOU FLOWN? ")) + while True: + try: + nmissions = int(input("HOW MANY MISSIONS HAVE YOU FLOWN? ")) - while nmissions >= 160: - print("MISSIONS, NOT MILES...") - print("150 MISSIONS IS HIGH EVEN FOR OLD-TIMERS") - nmissions = int(input("NOW THEN, HOW MANY MISSIONS HAVE YOU FLOWN? ")) + while nmissions >= 160: + print("MISSIONS, NOT MILES...") + print("150 MISSIONS IS HIGH EVEN FOR OLD-TIMERS") + nmissions = int(input("NOW THEN, HOW MANY MISSIONS HAVE YOU FLOWN? ")) + break + except ValueError: + # In the BASIC implementation this + # wasn't accounted for + print("TRY AGAIN...") + continue + else: + break if nmissions >= 100: print("THAT'S PUSHING THE ODDS!") @@ -73,7 +83,7 @@ def mission_failure() -> None: print() enemy_weapons = _stdin_choice( prompt="DOES THE ENEMY HAVE GUNS(1), MISSILES(2), OR BOTH(3)? ", - choices=weapons_choices.keys(), + choices=weapons_choices, ) # If there are no gunners (i.e. weapon choice 2) then @@ -83,9 +93,17 @@ def mission_failure() -> None: enemy_gunner_accuracy = 0.0 if enemy_weapons != "2": # If the enemy has guns, how accurate are the gunners? - enemy_gunner_accuracy = float( - input("WHAT'S THE PERCENT HIT RATE OF ENEMY GUNNERS (10 TO 50)? ") - ) + while True: + try: + enemy_gunner_accuracy = float( + input("WHAT'S THE PERCENT HIT RATE OF ENEMY GUNNERS (10 TO 50)? ") + ) + break + except ValueError: + # In the BASIC implementation this + # wasn't accounted for + print("TRY AGAIN...") + continue if enemy_gunner_accuracy < 10: print("YOU LIE, BUT YOU'LL PAY...") @@ -109,7 +127,7 @@ def play_italy() -> None: } target = _stdin_choice( prompt="YOUR TARGET -- ALBANIA(1), GREECE(2), NORTH AFRICA(3)", - choices=targets_to_messages.keys(), + choices=targets_to_messages, ) print(targets_to_messages[target]) @@ -125,7 +143,7 @@ def play_allies() -> None: } aircraft = _stdin_choice( prompt="AIRCRAFT -- LIBERATOR(1), B-29(2), B-17(3), LANCASTER(4): ", - choices=aircraft_to_message.keys(), + choices=aircraft_to_message, ) print(aircraft_to_message[aircraft]) @@ -137,10 +155,7 @@ def play_japan() -> None: first_mission = input("YOUR FIRST KAMIKAZE MISSION? (Y OR N): ") if first_mission.lower() == "n": return player_death() - - if random.random() > 0.65: - return mission_success() - return player_death() + return mission_success() if random.random() > 0.65 else player_death() def play_germany() -> None: @@ -152,7 +167,7 @@ def play_germany() -> None: } target = _stdin_choice( prompt="A NAZI, EH? OH WELL. ARE YOU GOING FOR RUSSIA(1),\nENGLAND(2), OR FRANCE(3)? ", - choices=targets_to_messages.keys(), + choices=targets_to_messages, ) print(targets_to_messages[target]) @@ -164,8 +179,7 @@ def play_game() -> None: print("YOU ARE A PILOT IN A WORLD WAR II BOMBER.") sides = {"1": play_italy, "2": play_allies, "3": play_japan, "4": play_germany} side = _stdin_choice( - prompt="WHAT SIDE -- ITALY(1), ALLIES(2), JAPAN(3), GERMANY(4): ", - choices=sides.keys(), + prompt="WHAT SIDE -- ITALY(1), ALLIES(2), JAPAN(3), GERMANY(4): ", choices=sides ) return sides[side]() From 62af4c0ab2a4c64633ec39e4f7da6ec5971392a0 Mon Sep 17 00:00:00 2001 From: Bernard Cooke Date: Fri, 14 Jan 2022 08:47:21 +0000 Subject: [PATCH 4/4] Correct looping/breaking in number of missions dialogue --- 12_Bombs_Away/python/bombs_away.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/12_Bombs_Away/python/bombs_away.py b/12_Bombs_Away/python/bombs_away.py index b3133ae3..5c591f44 100644 --- a/12_Bombs_Away/python/bombs_away.py +++ b/12_Bombs_Away/python/bombs_away.py @@ -51,14 +51,12 @@ def commence_non_kamikazi_attack() -> None: print("MISSIONS, NOT MILES...") print("150 MISSIONS IS HIGH EVEN FOR OLD-TIMERS") nmissions = int(input("NOW THEN, HOW MANY MISSIONS HAVE YOU FLOWN? ")) - break + break except ValueError: # In the BASIC implementation this # wasn't accounted for print("TRY AGAIN...") continue - else: - break if nmissions >= 100: print("THAT'S PUSHING THE ODDS!")