I already asked this question, but the script I provided was not compliant to PEP-8, so I rewrote the script for better readability.
I'm making a platformer game called Uni where this small character has to go as high as possible, in which the player constantly has to choose between jumping (W) going left or right (A-D) climbing down (S) or waiting (Just enter). A friend wanted a very difficult game that requires not timing but thinking to progress. Hence there are many rules that players learn throughout the game:
You may jump two characters over where you were initially at.
To go over a block, you must jump higher than it and from below.
To go over a ladder, you must jump at least as high as where it is from below.
When in the air, you can change direction (A-D) or you can stay where you are at.
When falling, you catch up speed.
Because it is a game based on Frames per Input, the number of "you"s you see represents the speed at which you just moved to reach your current position.
If you jump over a ladder exactly 3 characters above you, its as if you jumped over a block 2 characters above you.
If you jump over a ladder less than 3 characters above you, the next time you press enter you will "bounce" one character higher, because of the exess of velocity.
When you enter contact with a checkpoint, it activates it. When you die, you get teleported to the last checkpoint you activated (if any) and that same one dissappears. If there are no more activated checkpoints left, you simply die.
So the game works, and is very difficult but is possible. When I made it though, I wished for the layout to be randomly generated each time the script is runned. Now I realise that with so many rules, I have no idea how to get the layout to not just be randomly generated but also possible. The current game has a static layout.
Here is the updated script;
"""
A platformer game called Uni where this small character has to go as high as possible,
in which the player constantly has to choose between jumping (W)
going left or right (A-D) climbing down (S) or waiting (Just enter).
"""
import os
import time
debug = False
def get_key():
"""Cross-platform key input. Returns a single character."""
try:
import sys
import tty
import termios
fd = sys.stdin.fileno()
old = termios.tcgetattr(fd)
try:
tty.setraw(fd)
return sys.stdin.read(1)
finally:
termios.tcsetattr(fd, termios.TCSADRAIN, old)
except Exception:
return input("You may press W,A,S or D to move (or nothing, lets say if you are in freefall and you want to fall straight down), then hit Enter: ")[0].lower
# Game state variables
checkpoint1 = "\u235A"
portal = " "
my_list = []
window_height = 9
scrolling_y = 0
player_x = 1
player_char = "\u237E"
fellat = 0
dead = False
target = "a"
undertarget = "b"
right_undertarget = "c"
player_line = 0
key = "uhh"
def update_map():
"""Update the map with the current portal and checkpoint."""
# This is the layout of the level, which I would like to be automatically generated instead (while remaining possible for the player to complete)
global my_list, portal
my_list = [
" ",
" ",
" ",
" ",
f" \u2359 \u2359 {portal} ",
" \u2359 \u210D \u210D\u20AA\u20AA\u20AA\u20AA\u20AA\u20AA\u20AA\u20AA\u20AA",
" \u210D\u20AA \u2359 \u210D\u20AA ",
f" \u210D ",
" \u210D\u20AA\u20AA\u2359\u2359\u210D ",
" \u210D ",
" \u20AA \u20AA\u210D\u20AA ",
" \u2359\u210D \u2359 ",
" \u20AA \u2359 \u20AA\u20AA\u2359 ",
f" \u210D {checkpoint1} \u2359 ",
" \u20AA\u20AA\u20AA\u20AA\u20AA\u20AA\u20AA\u20AA ",
"_\u2359\u2359\u2359\u2359\u2359\u2359\u2359\u2359\u2359\u2359\u2359\u2359\u2359\u2359\u2359\u2359\u2359\u2359_",
" ",
" ",
" ",
" ",
" ",
" ",
]
update_map()
# Pre-scroll to initial visible area
while scrolling_y + window_height < len(my_list):
scrolling_y += 1
scrolling_y -= 6
def clear_screen():
"""Clear the terminal screen."""
os.system("cls" if os.name == "nt" else "clear")
def get_player_line(i):
"""Return the player's current line split into segments."""
line = my_list[i]
middle_pos = len(line) // 2
before = line[: middle_pos + player_x]
target_char = line[middle_pos + player_x]
after = line[(middle_pos + player_x + 1) :]
return before, target_char, after, middle_pos
def handle_checkpoint():
"""Check if player reached the checkpoint and activate it."""
global checkpoint1
if target == "\u235A" and scrolling_y == 7 and player_x == -3:
checkpoint1 = "\u06E9"
update_map()
def handle_player_physics():
"""Handle bouncing, falling, and spike death."""
global scrolling_y, fellat, dead, player_char
if (target == "\u210D" and key != "s") or (target == "\u20AA" and key == "s"):
scrolling_y -= 1
fellat = 0
clear_screen()
display_map()
return True # interrupts further display
elif undertarget == "\u2359" or target == "\u2359":
player_char = "\u237D"
dead = True
elif undertarget in [" ", "\u235A", "\u06E9"]:
scrolling_y += 1
elif right_undertarget == "\u20AA" and fellat == 2:
fellat = 0
return False
def display_map():
"""Display the visible portion of the map and handle player character."""
global target, undertarget, right_undertarget, middle, player_char, player_line
right_undertarget_set = False
clear_screen()
for i in range(scrolling_y, min(scrolling_y + window_height, len(my_list))):
if i == min(scrolling_y + window_height, len(my_list)) - 3:
# Player line
player_line = i
before, target, after, middle = get_player_line(i)
undertarget = my_list[i + 1][(len(my_list[i + 1]) // 2) + player_x]
if not right_undertarget_set:
right_undertarget = undertarget
right_undertarget_set = True
handle_checkpoint()
if handle_player_physics():
break
# Display the line with the player
print(before + target + "\b" + player_char + after)
else:
print(my_list[i])
if dead:
print("\n GAME OVER ")
# Main game loop
while not dead or checkpoint1 == "\u06E9":
clear_screen()
scrolling_y -= fellat
display_map()
if debug:
print(
f"PlayerX:{player_x} ScrollingY:{scrolling_y} "
f"Target:{target} Undertarget:{undertarget} Fellat:{fellat}"
)
key = input(">> ").lower()
# Respawn at checkpoint
if checkpoint1 == "\u06E9" and dead:
checkpoint1 = " "
update_map()
dead = False
fellat = 0
player_x = -3
scrolling_y = 7
clear_screen()
display_map()
player_char = "\u237E"
# Movement controls
if key == "q":
break
elif key == "w":
fellat = 3
elif key == "s" and scrolling_y + window_height < len(my_list):
scrolling_y += 1
fellat = 0
elif (
key == "d"
and player_x < 7
and my_list[player_line - (1 if fellat != 0 else 0)][(middle + player_x) + 1]
!= "\u20AA"
):
player_x += 1
elif (
key == "a"
and player_x > -10
and my_list[player_line - (1 if fellat != 0 else 0)][(middle + player_x) - 1]
!= "\u20AA"
):
player_x -= 1
if fellat > 0:
fellat -= 1
# Portal animation sequence
if scrolling_y == -2 and portal == " ":
for p in ["\u2223", "\u2336", "\u2180", "\U00002182"]:
portal = p
update_map()
clear_screen()
display_map()
time.sleep(0.15)
# Ending sequence
if player_x == 6 and scrolling_y == -2:
player_look_before = player_char
player_char = " "
update_map()
clear_screen()
display_map()
time.sleep(0.2)
for p in ["\u2180", "\u2336", "\u2223", " "]:
portal = p
update_map()
clear_screen()
display_map()
time.sleep(0.2)
time.sleep(1.8)
clear_screen()
time.sleep(6)
for p in [" ", "\u2223", "\u2336", "\u2180", "\U00002182", player_look_before]:
clear_screen()
player_char = p
r, c = os.get_terminal_size()
print("\n" * r + " " * (c // 2) + f"{player_char}")
time.sleep(0.2)
time.sleep(2.8)
clear_screen()
print("[The Creator] - Congradulations.")
time.sleep(2)
input("<<Continue>>")
clear_screen()
print("[The Creator] - You have proven yourself worthy of becoming citizen of unicode.")
time.sleep(2)
input("<<Continue>>")
for p in [player_char, "\U00002182", "\u2180", "\u2336", "\u2223", " "]:
clear_screen()
player_char = p
r, c = os.get_terminal_size()
print("\n" * r + " " * (c // 2) + f"{player_char}")
time.sleep(0.9)
time.sleep(3)
clear_screen()
time.sleep(4)
print("YOU WON")
time.sleep(0.5)
for msg in ["\n", "Coded by:", "Emmanuel Ghattas", "\n", f"Thanks for pl{player_look_before}ying!"]:
print(msg)
time.sleep(0.2)
time.sleep(7)
break
As you may notice, update_map() defines the layout of this level. I would like for the game layout (my_list[]) to be automatically generated through some logic. If anyone knows how to make this logic so that each time the player runs the script the layout is set through some rules and randomness while keeping it playable, or simply has ideas for the game, please let me know.