SIMON THE PIEMAN
kiwifarms.net
- Joined
- Apr 15, 2025
I remember implementing Pong back when I started to learn programming. It ran in the console window and it wasn't a great experience, but it was still Pong.
Follow along with the video below to see how to install our site as a web app on your home screen.
Note: This feature may not be available in some browsers.
How did you do that? That sounds really cool.I remember implementing Pong back when I started to learn programming. It ran in the console window and it wasn't a great experience, but it was still Pong.
This was years ago so I don't remember all the details, but I'll share what I remember.How did you do that? That sounds really cool.
#!/usr/bin/env ruby
require 'io/console'
DevInput='/dev/input/event3' #keyboard dev
QuitScanCode=1 #Esc
UpScanCode=30 #A
DownScanCode=44 #Z
FPS=60
Step=1.0/FPS
Tau=2*3.1415927
TauDeg=Tau/360
Ns=10.0**9
PaddleRatio=10.0
PaddleSpeed=Step*50
$returns=0
def center_text msg, col
mw=msg.length
sw=STDOUT.winsize[1]
STDOUT.goto col, sw/2-mw/2
puts msg
end
def now
Process.clock_gettime(Process::CLOCK_REALTIME, :nanosecond)
end
def halt val=0
STDIN.iflush
STDOUT.cooked!
STDOUT.goto 0,0
STDOUT.clear_screen
puts "Score: #{$returns}"
Kernel::exit(val)
end
def print_paddle rows,pos
hc=pos.clamp(0.0,1.0)
l=(rows*8/PaddleRatio).ceil
ht=(rows*hc-1/PaddleRatio/2*l).clamp(0,rows)
hb=(rows*hc+1/PaddleRatio/2*l).clamp(0,rows)
if(ht.floor>0)
STDOUT.goto ht.floor-1,0
print " "
end
if(hb.ceil-1<rows)
STDOUT.goto hb.ceil+1,0
print " "
end
(ht.floor .. hb.ceil).each{|y|
STDOUT.goto y,0
print "#"
}
end
process_winch=true
Signal.trap('SIGWINCH') do
process_winch=true
end
start_time=now
frame=0
x,y,theta,vel=0,0.8,-20.0*TauDeg,1
wx,hy=0,0
up_t,dn_t=0,0
padh=0.5
frame_start=now-1.0/FPS
STDIN.raw!
f=File.open(DevInput)
while true do
last_frame=frame_start
frame_start=now
would_block=false
until would_block do
begin
d=f.read_nonblock(24)
rescue IO::WaitReadable
would_block=true
end
if d
sec,usec,type,code,value=d.unpack("qqSSl")
if(type==1) #Key
case value # 30-a 44-z
when 1 #KeyDown
case code
when QuitScanCode
halt
when UpScanCode
up_t=sec*Ns+usec*1000
when DownScanCode
dn_t=sec*Ns+usec*1000
end
when 0 #KeyUp
zz=((sec*Ns+usec*1000)-last_frame)/Ns*PaddleSpeed
case code
when UpScanCode
padh-=zz
up_t=0
when DownScanCode
padh+=zz
dn_t=0
end
end
end
end
end
if(up_t>0 && up_t<frame_start)
padh-=(frame_start-up_t)/Ns*PaddleSpeed
up_t=frame_start
end
if(dn_t>0 && dn_t<frame_start)
padh+=(frame_start-dn_t)/Ns*PaddleSpeed
dn_t=frame_start
end
# recalc each frame because we don't know
w=STDOUT.winsize[1]-1
h=STDOUT.winsize[0]
ew=1/w
eh=1/h
halt if(h<13)
if process_winch
STDOUT.clear_screen
process_winch=false
end
STDOUT.goto hy,wx
print " "
dx=Math.cos(theta)*vel*Step
dy=Math.sin(theta)*vel*Step
x+=dx
y+=dy
if(x>=(1-ew*2))
x=1-ew*2
theta=(100 + rand(1600)/10)*TauDeg
elsif(x<ew)
if((y-padh).abs<(1/PaddleRatio))
x=2*ew
theta=((y-padh)/(1/PaddleRatio/2)*66)*TauDeg
$returns+=1
else
halt
end
end
if(y>(1-eh))
y=1-eh
theta=-theta
elsif(y<eh)
y=eh
theta=-theta
end
wx=(w-2)*x
hy=h*y
STDOUT.goto hy,wx
print "()"
padh=padh.clamp(0.0,1.0)
print_paddle h,padh
STDOUT.goto h-1,w-10
desired_time=(start_time/Ns) + frame.to_f/FPS - (now/Ns)
print "#{ "%1.1f" % ( (1.0/FPS) / ( 1.0/FPS - desired_time ) ) }x "
STDOUT.goto 0,w-20
print " Score: #{$returns} "
sleep desired_time if desired_time > 0
frame+=1
end
halt
That sure does look better than NCurses, given that the interface is at least comprehensible and probably isn't buggy (I think they might have done something recently that they consider a fix, but its a 30 year old library ffs).Termbox is pretty much always the way to go instead of ncurses these days. My chat client uses a TUI library that applies the termbox concepts nicely.
I haven't noticed any of that in my usage of it. Whatever the Go library I use does with the termbox concept is done in a quick enough way that I can justify redrawing the whole buffer to handle chat message edits.does it not have a problem with flashing?
I have, twice at least. I have a note on it here, all it says is:has anyone tried ftxui? looks pretty good
Not sure what my actual problem was.meh on all fronts at best
I just had a look at the python curses module and it looks like it's just a thin wrapper around the C interface. I would have expected something a little more user friendly from Python...I would say "try ncurses and make a TUI", but don't because NCurses is shit. I'm sure that there is some python TUI library that's fine, but I can't give you a recommendation on that front.
import better_curses
# class handles init and clean up
main_window = better_curses.Window() # defaults; echo=False, cbreak=True, keypad=True
# no need for "ch" and "str" variants as python does not have chars
main_window.print("E") # can also accept x, y kwargs
The code in the try block should be limited to only what is actually being checked, it doesn't matter much here, but, if some other function in the block threw a ValueError then the user would get an unhelpful message about invalid input (and, in some cases, the program state may become corrupted). The while loop should also only contain what is necessary for handling the input. There is also repeated code at the start/end of both if/else branches that can be moved out (i.e. the code does not depend on the condition).Speaking of the gamba game, here's the updated version with all the sleeps, if anyone is interested.
def gamba_game(game_state):
# function vars
while True:
print(f"you have ${game_state.cash}")
bet = input("enter bet\n$")
try:
stake = int(bet)
except ValueError:
print("Invalid Input")
continue
if stake > game_state.cash:
print("too poor")
else:
break
# display roll animation
if game_roll <= win_chance:
# do win stuff
else:
# do lose stuff
# print player balance
Like this?The code in the try block should be limited to only what is actually being checked, it doesn't matter much here, but, if some other function in the block threw a ValueError then the user would get an unhelpful message about invalid input (and, in some cases, the program state may become corrupted). The while loop should also only contain what is necessary for handling the input. There is also repeated code at the start/end of both if/else branches that can be moved out (i.e. the code does not depend on the condition).
This makes the intent clearer and reduces the level of indentation required, which makes the code easier to read and understand (and maintain).
def gamba_game(game_state):
game_roll = random.randint(1,100)
win_chance = 49
display_roll = 100 - game_roll
roll_count = 1
while True:
bet = input("how much would you like to bet?: $")
time.sleep(0.2)
try:
stake = int(bet)
if stake > game_state.cash:
print("You don't have that much money!")
time.sleep(0.2)
else:
print(f"You bet ${stake}")
break
except ValueError:
print("Invalid input")
if game_roll <= win_chance:
print("0",end="")
while roll_count < display_roll + 1:
print(f"\r{roll_count}", end="")
time.sleep(0.02)
roll_count = roll_count + 1
print("\nYes!")
time.sleep(0.2)
game_state.wins = game_state.wins + 1
game_state.cash = game_state.cash + (stake)
print(f"You won ${stake}")
time.sleep(0.2)
print(f"your balance is ${game_state.cash}")
time.sleep(0.2)
else:
print("0",end="")
while roll_count < display_roll + 1:
print(f"\r{roll_count}", end="")
time.sleep(0.02)
roll_count = roll_count + 1
print("\nNo")
game_state.losses = game_state.losses + 1
game_state.cash = game_state.cash - stake
print(f"You lost ${stake}")
time.sleep(0.2)
print(f"your balance is ${game_state.cash}")
time.sleep(0.2)
import random
from colorama import Fore,Style
import time
class Gamestate:
def __init__(self,wins=0,losses=0,cash=100):
self.wins = wins
self.losses = losses
self.cash = cash
@staticmethod
def start_game():
return Gamestate()
def gamba_slots(game_state):
print('Enter "payout" into the bet line to view the payout list')
time.sleep(0.2)
payout_jackpot = "\U0001F95D \U0001F95D \U0001F95D | Bet x 300"
payout_4 = Fore.RED + Style.BRIGHT + " 7 7 7 " + Style.RESET_ALL + "| Bet x 30"
payout_3 = "\U0001F34B \U0001F34B \U0001F34B | Bet x 10"
payout_2 = "\U0001F347 \U0001F347 \U0001F347 | Bet x 5"
payout_1 = "\U0001F352 \U0001F352 \U0001F352 | Bet x 2"
payout_list = [payout_jackpot,payout_4,payout_3,payout_2,payout_1]
wheels = [[1,1,1,2,2,3,3,4,4,5], [1,1,1,2,2,3,3,4,4,5], [1,1,1,2,2,3,3,4,4,5]]
icons = {"5" : "\U0001F95D",
"4" : (Fore.RED + "\033[1m7\033[0m"),
"3" : "\U0001F34B",
"2" : "\U0001F347",
"1" : "\U0001F352"}
spin_result = ""
display_result = ""
while True:
bet = input("how much would you like to bet?: $")
time.sleep(0.2)
if bet.lower() == "payout":
for payout in payout_list:
time.sleep(0.2)
print(payout)
else:
try:
stake = int(bet)
if stake > game_state.cash:
print("You don't have that much money!")
time.sleep(0.2)
else:
print(f"You bet ${stake}")
break
except ValueError:
print("Invalid input")
game_state.cash = game_state.cash - stake
for wheel in wheels:
number = str(random.choice(wheel))
spin_result += number
display_result += icons[number] + " "
print(f"\r{display_result}", end = " ")
time.sleep(0.5)
print("")
if spin_result == "555":
print("You won the jackpot!")
time.sleep(0.02)
game_state.cash = game_state.cash + (stake * 300)
print(f"You won ${stake * 300}")
time.sleep(0.02)
print(f"Your balance is ${game_state.cash}")
time.sleep(0.02)
elif spin_result == "444":
game_state.cash = game_state.cash + (stake * 30)
print(f"You won ${stake * 30}")
time.sleep(0.02)
print(f"Your balance is ${game_state.cash}")
time.sleep(0.02)
elif spin_result == "333":
game_state.cash = game_state.cash + (stake * 10)
print(f"You won ${stake * 10}")
time.sleep(0.02)
print(f"Your balance is ${game_state.cash}")
time.sleep(0.02)
elif spin_result == "222":
game_state.cash = game_state.cash + (stake * 5)
print(f"You won ${stake * 5}")
time.sleep(0.02)
print(f"Your balance is ${game_state.cash}")
time.sleep(0.02)
elif spin_result == "111":
game_state.cash = game_state.cash + (stake * 2)
print(f"You won ${stake * 2}")
time.sleep(0.02)
print(f"Your balance is ${game_state.cash}")
time.sleep(0.02)
else:
print("Dude, this shit is so rigged")
time.sleep(0.02)
print(f"Your balance is ${game_state.cash}")
time.sleep(0.02)
def gamba_game(game_state):
game_roll = random.randint(1,100)
win_chance = 49
display_roll = 100 - game_roll
roll_count = 1
while True:
bet = input("how much would you like to bet?: $")
time.sleep(0.2)
try:
stake = int(bet)
if stake > game_state.cash:
print("You don't have that much money!")
time.sleep(0.2)
else:
print(f"You bet ${stake}")
break
except ValueError:
print("Invalid input")
if game_roll <= win_chance:
print("0",end="")
while roll_count < display_roll + 1:
print(f"\r{roll_count}", end="")
time.sleep(0.02)
roll_count = roll_count + 1
print("\nYes!")
time.sleep(0.2)
game_state.wins = game_state.wins + 1
game_state.cash = game_state.cash + (stake)
print(f"You won ${stake}")
time.sleep(0.2)
print(f"your balance is ${game_state.cash}")
time.sleep(0.2)
else:
print("0",end="")
while roll_count < display_roll + 1:
print(f"\r{roll_count}", end="")
time.sleep(0.02)
roll_count = roll_count + 1
print("\nNo")
game_state.losses = game_state.losses + 1
game_state.cash = game_state.cash - stake
print(f"You lost ${stake}")
time.sleep(0.2)
print(f"your balance is ${game_state.cash}")
time.sleep(0.2)
def gamba_start(game_state):
print(f"Welcome to the casino!")
time.sleep(0.2)
print(f"You have ${game_state.cash}.")
time.sleep(0.2)
while True:
play = input(f"Would you like to play?(yes/no): ")
time.sleep(0.2)
play = play.lower()
match play:
case "yes"|"y":
gamba_sesh(game_state)
break
case "no"|"n":
print("That's probably a good idea")
break
case _:
print("Invalid input, please try again")
def gamba_sesh(game_state):
while True:
if game_state.cash <= 0:
print("Dude I just lost it all")
time.sleep(0.2)
print(f"wins {game_state.wins}")
time.sleep(0.2)
print(f"losses {game_state.losses}")
break
else:
print("The games available are slots and yesno (more coming soon!)")
time.sleep(0.2)
print("To play slots enter (s), for yesno enter (y) and to leave the casino enter (q)")
time.sleep(0.2)
play = input("What would you like to do?: ")
time.sleep(0.2)
play = play.lower()
match play:
case "s"|"(s)":
gamba_slots_play_again(game_state)
case "y"|"(y)":
gamba_game_play_again(game_state)
case _:
print("One more game of dice and I'm out!")
time.sleep(0.2)
gamba_game(game_state)
def gamba_slots_play_again(game_state):
gamba_slots(game_state)
while True:
if game_state.cash <= 0:
break
else:
play_again = input("Play again? (yes/no): ")
time.sleep(0.2)
play_again = play_again.lower()
match play_again:
case "yes"|"y":
gamba_slots(game_state)
case "no"|"n":
print("Slots exited")
time.sleep(0.2)
break
case _:
print("Invalid Input")
time.sleep(0.2)
def gamba_game_play_again(game_state):
gamba_game(game_state)
while True:
if game_state.cash <= 0:
break
else:
play_again = input("Play again? (yes/no): ")
time.sleep(0.2)
play_again = play_again.lower()
match play_again:
case "yes"|"y":
gamba_game(game_state)
case "no"|"n":
print("Dice exited")
time.sleep(0.2)
break
case _:
print("Invalid Input")
time.sleep(0.2)
game_state_start = Gamestate.start_game()
gamba_start(game_state_start)
Complexity in a system's design for complexity's sake is niggerlicious. Everything has a cost, if your reasoning for something is for the complexity, you're spending computational resources and effort without gaining anything from that, not even anything abstract like generally applicable technical knowledge.Did Terry have any unironically good programing advice?
C and C++ are distinct and different programming languages, there is no such thing as C/C++. Sure you can do C-isms in C++, but if you're using a C++ compiler, you're using C++ even if you manually call malloc and free. Yes this is an autistic distinction, but it is a very important one because of C's looser and more implicit type system.C/C++ otherwise
The real answer is that if doing it in python feels good and keeps you enthusiastic, keep doing it in python. Push in the direction that serves you, not some chuds on a forum. Whatever keeps you on the path moving towards whatever your goal was with picking up programming.How will I know that I'm comfortable enough?
Not saying you should post it and accidentally phonebook yourself in the commit logs, but are you using version control software like git? If you aren't, it is very worthwhile learning even if you never push to a remote repository somewhere. It means you can always revert to a previous working code state so long as you committed it.here's the updated game
Use more functions to reduce the number of repeated fragments.Alright, here's the updated game with slots.
def _print_sleep(text: str, sleep_seconds=0.02):
print(text)
time.sleep(sleep_seconds)
import typing
def play_again(game_run: typing.Callable, name: str, game_state: Gamestate):
game_run(game_state)
while True:
if game_state.cash <= 0:
break
else:
play_again = input("Play again? (yes/no): ")
time.sleep(0.2)
play_again = play_again.lower()
match play_again:
case "yes"|"y":
game_run(game_state)
case "no"|"n":
_print_sleep(f"{name} exited")
break
case _:
_print_sleep("Invalid Input")
play_again(gamba_slots, "Slots", game_state)
def func_name(name):
def wrapper(fn):
fn.name = name
return fn
return wrapper
@func_name("Slots")
def slots(game_state):
...
_print_sleep(f"{game_run.name} exited")
JACKPOT = 300
WINNING_SPINS = {
"555": JACKPOT,
"444": 30,
# rest
}
win_factor = WINNING_SPINS.get(spin_result, -1)
winnings = stake * win_factor
# display if jackpot
if win_factor == JACKPOT:
_print_sleep("You won the jackpot!")
if win_factor > 0:
_print_sleep(f"You won {winnings}")
else:
_print_sleep("Dude this shit is so rigged")
game_state.cash += winnings
_print_sleep(f"Your balance: {game_state.cash}")
import gamba
def do_something(*a, **kw):
gamba.slots( ...... )
Another thing to learn is virtual environments. @Cranjis McBasketball even if you're currently using the system python (what's your OS?), eventually you will have separate projects and separate sets of libraries in each with their specific versions. A library you want to use can be dependent on more libraries it wants to use, these can change from version to version, each of those have versions, too. There are ways to organize these sets and describe each set to allow necessary updates and disallow potentially breaking updates. I use poetry, which is complicated and gay, but a good alternative for beginners is venv (built-in) + pip tools https://github.com/jazzband/pip-tools .Not saying you should post it and accidentally phonebook yourself in the commit logs, but are you using version control software like git? If you aren't, it is very worthwhile learning even if you never push to a remote repository somewhere. It means you can always revert to a previous working code state so long as you committed it.
I recommend people learn C or C++ as their first language. I wasn't lumping them in as one thing.C and C++ are distinct and different programming languages, there is no such thing as C/C++. Sure you can do C-isms in C++, but if you're using a C++ compiler, you're using C++ even if you manually call malloc and free. Yes this is an autistic distinction, but it is a very important one because of C's looser and more implicit type system.
That's my mine gripe with modern C++ crowd. C++ is a mess, but a lost of things start to make some sense if you go bottom up in design, starting with C, then RAII. and only after that you go for modern features.Then probably C, since the fundamentals are the same, but there is simply less stuff to get confused by.
For example, I remember being baffled by smart pointers because they were presented to me before having a firm grasp on raw pointers.
The term "anti-pattern" makes me unreasonably angry."muh bad practices"