UnBooks:Fun with Python
Jump to navigation
Jump to search
Python has a highly flexible syntax, allowing a diverse range of design patterns and paradigms. Python has also absorbed features from a bunch other languages, allowing programmers proficient in different domains to get creative with Python. Below is an implementation of the text-based terminal strategy game Fun Science for Kids in Python 3.11, in order to gain a deeper understanding of its uniquely weird syntax and features.
Source[edit | edit source]
## FSfK, a multiplayer text adventure & research industry simulation
## Moves: 'r' - research
## 'p' - plagiarize
## 'a [TARGET]' - attack player/group of name TARGET
## 'g [PLAYER]' - group with player PLAYER
## 'd' - disband current group if in one
## See the detailed rules at [[Fun_Science_for_Kids]]
## This file is entirely written in Notepad on a Windows machine.
## Linux users please pay the CRLF no mind.
#ifndef ENCYCLOPEDIA_H
#define ENCYCLOPEDIA_H
#include <iostream>
import sys
class Numms:
"""
Constants
"""
# status codes
YEP = 1
NOPE = 0
NO_NO_NO_NEVER_IN_AN_ETERNITY = -1
# player
UNINITALIZED = 0
FAILED = 2
INACTIVE = 3
ACTIVE = 4
GROUPED = 5
ascii_uppercase = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
cmp_attribs = ["n_members", "papers", "reput"]
def wtf(msg):
print("WTF! Program terminated: %s" %msg)
exit(Numms.NO_NO_NO_NEVER_IN_AN_ETERNITY)
def wth(msg):
print("WTH? Smth's wrong: %s" %msg)
def wti(msg):
print("We tersely inform you: %s" %msg)
class Entity:
"""
Stores player stats.
"""
def __init__(self, id):
self.id = id
self.status = Numms.UNINITALIZED
def init_real(self, name):
self.name = name
self.members = set()
self.members.add(self.id)
self.n_members = 1
self.papers = 3 # individuals start with 3/0
self.reput = 0
self.status = Numms.ACTIVE
def init_real_real(self, name, members, papers, reput):
self.name = name
self.members = members
self.n_members = len(members)
self.papers = papers
self.reput = reput
self.status = Numms.INACTIVE # groups have a 1-round cooldown
def getter(self):
wtf("does a getter do")
def display_stats(self):
print("<Entity %s> papers %d, reput %d, status %d" %(self.name, self.papers, self.reput, self.status)) # print is best function
def on_death(self, cause):
print("<Entity %s> Oh no! I failed due to %s." %(self.name, cause))
self.__del__() # if it doesn't work try commenting out this (and other) line(s)
class RandomShit:
"""
utils, play order sorter, grouping, disbanding
"""
def __init__(self, max_papers):
self.entities = dict()
self.n_entities = 0
self.max_papers = max_papers
self.play_order = []
def add_entity(self, e):
self.entities[e.id] = e
self.n_entities += 1
def which_group_are_you_in(self, e_unknown):
# legacy fuinctoin.
for k, e in self.entities:
if len(e.members)==1: # not group
continue
if e_unknown.name in e.members:
return(e)
wth("Player %s is not in any group." %e_unknown.name)
return None # failed to find
def kill_child(self, id_victim, cause):
if not id_victim in self.entities:
wth("Entity %s does not exist." %id_victim)
return
self.entities[id_victim].on_death(cause)
self.entities.pop(id_victim)
def test_for_dead(self):
e_deads = []
for k in self.entities:
e = self.entities[k]
if e.papers <= 0 or e.reput <= -3 or e.status == Numms.FAILED:
e_deads = e_deads + list(e.members)
for k in e_deads:
self.kill_child(k, "for sacrifice")
def test_for_paper_overflow(self, e_last_acted):
sum_papers = 0
for k in self.entities:
sum_papers += self.entities[k].papers
if sum_papers > self.max_papers:
wti("The research field is now saturated at %d papers! o.o" %self.max_papers)
e_last_acted.papers = e_last_acted.papers - (sum_papers - self.max_papers)
def g(self, e_agent, e_patient):
e_group = Entity(id = self.n_entities)
#m = e_agent.members.add(e_patient.members) # "Unhashable type"? Wtf is a hash
m = set()
for member in e_agent.members:
m.add(member)
for member in e_patient.members:
m.add(member)
n = "".join([self.entities[id].name for id in sorted(m)])
p = e_agent.papers + e_patient.papers
r = e_agent.reput + e_patient.reput
e_group.init_real_real(n, m, p, r) # passes arguments n, m, p, and r to method init_real_real of object e_group
e_agent.reput += 1
e_agent.status = Numms.GROUPED
e_patient.status = Numms.GROUPED
id = self.n_entities
self.entities[id] = e_group
self.n_entities += 1
def d(self, e_agent):
e_group = which_group_are_you_in(e_agent)
if not e_group:
return
m = e_group.members
m = m - e_agent.name
e_agent.reput -= 2
e_agent.papers = e_group.papers # I commented the code very well
for k in m:
self.entities[k].papers = 1
e_agent.papers -= 1
self.entities.pop(e_group.name)
self.n_entities -= 1
def __is_e1_better_than_e2_if_yes_return_one_if_no_return_negative_one(self, e1, e2):
for attrib in cmp_attribs:
value1 = e1.__getattribute__(attrib)
value2 = e2.__getattribute__(attrib)
if value1 < value2:
return(-1)
elif value1 > value2:
return(1)
return(-1) # all stats identical
def parse_play_order(self):
entities_temp = dict(self.entities)
self.play_order = []
for i in range(self.n_entities):
e_temp = Entity(id = -1)
e_temp.init_real_real("dummy", set(), 0, 0)
for k in entities_temp:
if self.__is_e1_better_than_e2_if_yes_return_one_if_no_return_negative_one(entities_temp[k], e_temp)==1:
e_temp = entities_temp[k]
if e_temp.id!=-1:
entities_temp.pop(e_temp.id)
self.play_order.append(e_temp.id)
else:
wth("We encountered an unexpected issue. The game can keep running but may crash at any time.") # fucking fuckity fuck!!
class DataManager:
"""
Manages data.
"""
def __init__(self):
if len(sys.argv) == 1:
wti("Usage: FSfK.py [number of players]")
exit(0)
self.n_players = int(sys.argv[1])
if self.n_players<=0 or self.n_players>=len(ascii_uppercase):
wtf("I am batman")
self.r = RandomShit(max_papers = 5 * self.n_players)
for i in range(self.n_players):
name = ascii_uppercase[i]
e_player_i = Entity(id = i)
e_player_i.init_real(name)
self.r.add_entity(e_player_i)
is_win = False
self.round = 0
while not is_win:
self.new_round()
self.r.parse_play_order()
#print("sequence of play is", self.r.play_order)
for id in self.r.play_order:
if self.r.entities[id].status != Numms.ACTIVE:
continue
ret = Numms.NOPE
while ret != Numms.YEP:
ret = self.parse_cmd(self.r.entities[id])
self.r.test_for_dead()
is_win = self.function_that_does_this()
self.round += 1
def new_round(self):
print("Round %d" %self.round)
for k in self.r.entities:
e = self.r.entities[k] # weak copy, changes to this also changes the original
if e.status!=Numms.GROUPED:
e.status = Numms.ACTIVE
self.r.entities[k].display_stats()
def get_id_from_name(self, name_entity):
for k in self.r.entities:
e = self.r.entities[k]
if e.name==name_entity:
return(e.id)
return(None)
def function_that_does_this(self):
if self.r.n_entities<=2:
return(True)
else:
return(False)
def parse_cmd(self, e_agent):
print("<%s> >>> " %e_agent.name, end="")
cmd = input()
if cmd=="end":
wti("Exiting game...")
exit(0)
elif cmd=="":
wth("cmd is empty.")
return(Numms.NOPE)
move = cmd[0]
if e_agent.status==Numms.GROUPED and move!='d':
wth("Your group has acted and you can't act, however you can disband")
return(Numms.NOPE)
if move=='r':
e_agent.papers += e_agent.n_members
e_agent.reput += e_agent.n_members
#wti("You made a contribution to science. +1/+1")
self.r.test_for_paper_overflow(e_agent)
return(Numms.YEP)
elif move=='p':
e_agent.papers = e_agent.papers + e_agent.n_members + 1
e_agent.reput -= 1
#wti("You got all the plagiarizz +2/-1")
self.r.test_for_paper_overflow(e_agent)
return(Numms.YEP)
elif move=='a':
if not " " in cmd:
wth("Please specify the name of the target to attack")
return(Numms.NOPE)
name_patient = cmd.split(" ")[1]
id_patient = self.get_id_from_name(name_patient)
if not id_patient:
wth("There is no player named %s." %name_patient)
return(Numms.NOPE)
e_patient = self.r.entities[id_patient]
if e_agent.name==e_patient.name:
wti("You used self-deprecation, which is fine")
e_agent.reput -= 1
e_patient.papers -= e_agent.n_members
#wti("Great job! Your attack got their papers retracted.")
return(Numms.YEP)
elif move=='d':
status = -1
if status!=2:
wth("Player is not in a group.")
return(Numms.NOPE)
self.r.d(e_agent)
#wti("The group has been disbanded.")
return(Numms.YEP)
elif move=='g':
if not " " in cmd:
wth("Please specify an entity to group with")
return(Numms.NOPE)
name_patient = cmd.split(" ")[1]
id_patient = self.get_id_from_name(name_patient)
e_patient = self.r.entities[id_patient]
if e_agent.name==name_patient:
wth("Cannot group with yourself.")
return(Numms.NOPE)
elif not id_patient:
wth("There is no player named '%s'." %name_patient)
return(Numms.NOPE)
elif e_patient.status==Numms.GROUPED:
wth("Player %s is already in a group." %name_patient)
return(Numms.NOPE)
elif e_agent.n_members>=2 or e_patient.n_members>=2:
wth("Group coalescing requires a VIP subscription.")
return(Numms.NOPE)
self.r.g(e_agent, e_patient)
#wti("Academic friendship has formed")
return(Numms.YEP)
else:
wth("Unknown command.")
return(Numms.NOPE)
game = DataManager() # all the code you'll ever need
#endif // FSfK.py
It's a joke[edit | edit source]
This is the worst... thing I've ever written. Considering it displays all sorts of undocumented behavior, this implementation is very buggy and generally unplayable, but bugs have a mind of their own and they don't want to get fixed. Also remember: NO WARRANTY [sic] is given.