diff options
-rw-r--r-- | README.md | 30 | ||||
-rw-r--r-- | explore.py | 97 | ||||
-rw-r--r-- | inv.py | 1 | ||||
-rw-r--r-- | isportal.py | 56 | ||||
-rw-r--r-- | isrock.py | 55 | ||||
-rw-r--r-- | rocks.py | 41 | ||||
-rw-r--r-- | sorter.py | 96 | ||||
-rw-r--r-- | worlds/home | 196 |
8 files changed, 572 insertions, 0 deletions
diff --git a/README.md b/README.md new file mode 100644 index 0000000..6ec72dd --- /dev/null +++ b/README.md @@ -0,0 +1,30 @@ +# rocks + +An experimental terminal-based game about rocks + +## File Structure and Entry Point +``` +| rocks.py ; the entry point, runs curses and manages explore/graphics +| explore.py ; provides Explore, a class which manages saving, loading, movement, some graphics, inventory, and rock positioning +| inv.py ;one line: "quantity = 0". This sections off the inventory into inv.quantity for explore and could permit more extension in the future +| isrock.py ; makes use of sorter.py and manages all items as rocks (including portals to prevent intersection --- however, "type of rock" is noted) +| isportal.py ; also makes use of sorter.py. manages all portals' saving, movement, and interlinking +| sorter.py ; creates a new, efficient data type that automatically insorts coordinates with bisection and allows indexing, removal, etc. +| README.md ; you're reading it right now +| worlds ; a directory which holds all files created by save data +``` + +## Necessary Tools + +Python3.7.2 was used to develop this app, and to run it, `curses`, `hashlib`, and `math` must be available. + +## Controls + +`rocks.py` runs as a full window terminal application with a few controls, namely: +- `q`: quits the program and returns to normal terminal mode +- `s`: saves at a requested file location in worlds +- `l`: loads from a file in worlds (if not available, does nothing) +- arrow keys: moves cursor around world and through portals +- `.` picks up rocks +- `/` puts down rocks in your inventory +- `\\` puts down portals (cost 10 rocks) diff --git a/explore.py b/explore.py new file mode 100644 index 0000000..fa8239c --- /dev/null +++ b/explore.py @@ -0,0 +1,97 @@ +import isrock +import isportal +from math import floor +from curses import KEY_LEFT as left, KEY_RIGHT as right, KEY_DOWN as down, KEY_UP as up +import inv + +def splitlist(liststr, strsplit): + newlist = [] + sublist = [] + for item in liststr: + if item == strsplit: + newlist.append(sublist) + sublist = [] + else: + sublist.append(item) + newlist.append(sublist) + return newlist + +class Explore: + def __init__(self, density, x=0, y=0): + self.density = density + self.x = x + self.y = y + + def get_area(self, dim): + out = '' + for y in range(dim[0]): + y += self.y + for x in range(floor(dim[1]/2)): + x += self.x + pos = isrock.isrock(self.density, (x,y), typereq=True) + if not pos[0]: + out += ' ' + elif pos[1] == 'n': + out += '*' + else: + out += '\\' + out += ' ' + out = out[:-1] + out += '\n' + return out[:-1] + + def displace(self, dim, dis): + self.move((self.x + dim[0], self.y + dim[1]), dis) + + def move(self, dim, dis): + self.x, self.y = dim[0], dim[1] + dim = (dim[0]+dis[1], dim[1]+dis[0]) + if isportal.isportal(dim): + dim = isportal.link(dim) + self.x, self.y = dim[0]-dis[1], dim[1]-dis[0] + + def interpretkey(self, key): + return {up:(0,-1), left:(-1,0), down:(0,1), right:(1,0)}[key] + + def rockmod(self, dim, state): + dim = (self.x + dim[1], self.y + dim[0]) + if state != isrock.isrock(self.density, dim): + if state and inv.quantity <= 0: + return + isrock.modrock(dim, state) + inv.quantity += -1 if state else 1 + + def getinv(self): + return inv.quantity + + def load(self, file): + try: + f = open('./worlds/'+str(file)[2:-1],'r') + data = [line[:-1] for line in f.readlines()] #removes newlines + f.close() + except: + return "File Doesn't Exist" + loc = [int(num) for num in data[0].split(' ')] + self.x, self.y, inv.quantity = loc[0], loc[1], loc[2] + data = splitlist(data[1:], '______') + isrock.load(data[0]) + isportal.load(data[1]) + + def save(self, file): + f = open('./worlds/'+str(file)[2:-1],'w') + data = str(self.x) + ' ' + str(self.y) + ' ' + str(inv.quantity) + '\n' + data += isrock.save() + data += '______\n' + isportal.save() + f.write(data) + f.close() + + def setportal(self, dim): + dim = (dim[1]+self.x, dim[0]+self.y) + if not isrock.isrock(self.density, dim) and inv.quantity >= 10: + inv.quantity -= 10 + isrock.modrock(dim, True, 'p') + isportal.modportal(dim) + elif isrock.isrock(self.density, dim, typereq=True)[1] == 'p': + inv.quantity += 10 + isrock.modrock(dim, False) + isportal.modportal(dim) @@ -0,0 +1 @@ +quantity = 0 diff --git a/isportal.py b/isportal.py new file mode 100644 index 0000000..3e88968 --- /dev/null +++ b/isportal.py @@ -0,0 +1,56 @@ +from sorter import Sorter + +portals = Sorter() +links = [] +lastportal = None #dim of last portal + +def conlast(cur): + global lastportal + ind = portals.index(cur) + if lastportal == None: #self-connect and set as lastportal + links[ind] = cur + lastportal = cur + else: #connect to last portal, connect last portal to self, and set lastportal as None + links[ind] = lastportal + links[portals.index(lastportal)] = cur + lastportal = None + +def isportal(dim): + if dim in portals: + return True + return False + +def link(dim): + return links[portals.index(dim)] + +def unlink(dim): #remove portal from main list, and update the link of its pair + global lastportal + plink = links[portals.index(dim)] + if plink != dim: + conlast(plink) + else: + if dim == lastportal: + lastportal = None + +def modportal(dim): + if dim in portals: #remove portal + unlink(dim) + links.pop(portals.remove(dim)) + else: #add portal + links.insert(portals.insert(dim),()) + conlast(dim) + +def load(data): + lastportal = None if data[0] == 'None' else int(data[0]) + portals = Sorter() + links = [] + for line in data[1:]: + line = [int(num) for num in line.split(' ')] + ind = portals.insert((line[0],line[1])) + links.insert(ind, (line[2], line[3])) + +def save(): + outtext = str(lastportal) + '\n' + for portalind in range(len(portals)): + outtext += str(portals[portalind][0]) + ' ' + str(portals[portalind][1]) + ' ' + str(links[portalind][0]) + ' ' + str(links[portalind][1]) + '\n' + return outtext diff --git a/isrock.py b/isrock.py new file mode 100644 index 0000000..085b25b --- /dev/null +++ b/isrock.py @@ -0,0 +1,55 @@ +from hashlib import md5 +from sorter import Sorter + +rockmodp = Sorter() #positions of rockmod +rockmodv = [] #values of rockmod +rockmodt = [] #rock type (to include portals) + +def modrock(dim, state, type='n'): #type is by default normal; p is portal + if state and type == 'n': + type = 'n' + if dim in rockmodp: + loc = rockmodp.index(dim) + rockmodv[loc] = state + rockmodt[loc] = type + else: + loc = rockmodp.insert(dim) + rockmodv.insert(loc, state) + rockmodt.insert(loc, type) + + +def isrock(density, dim, typereq=False): #if typereq, returns if a rock or portal + x, y = dim[0], dim[1] + m = md5() + m.update(intbyt(x)) + m.update(intbyt(y)) + rockpres = (bytint(m.digest()[:2])/(256**2) < density) + if dim in rockmodp: + loc = rockmodp.index(dim) + rockpres = rockmodv[loc] + if typereq: + return (rockpres, rockmodt[loc]) + if typereq: + return (rockpres, 'n') + return rockpres + +def intbyt(num): + return int(num).to_bytes(8, 'big', signed=True) + +def bytint(byt): + return int.from_bytes(byt, byteorder='big') + +def save(): + outtext = '' + for mvind in range(len(rockmodp)): + outtext += str(rockmodp[mvind][0]) + ' ' + str(rockmodp[mvind][1]) + ' ' + str(rockmodv[mvind]) + ' ' + str(rockmodt[mvind]) + '\n' + return outtext + +def load(data): + global rockmodp, rockmodv, rockmodt + rockmodp, rockmodv, rockmodt = Sorter(), [], [] + for line in data: + line = line.split(' ') + ind = rockmodp.insert((int(line[0]), int(line[1]))) + rockmodv.insert(ind, line[2] == 'True') + rockmodt.insert(ind, line[3]) diff --git a/rocks.py b/rocks.py new file mode 100644 index 0000000..975c799 --- /dev/null +++ b/rocks.py @@ -0,0 +1,41 @@ +from explore import Explore +import curses +from math import floor + +explore = Explore(0.0025) + +screen = curses.initscr() +curses.noecho() #makes keys not put text on screen +curses.cbreak() #removes buffer, allowing prog to receive control immediately +screen.keypad(True) #allows arrow keys and things to work +screen.scrollok(False) #disables scrolling + +key = '' +while key != ord('q'): + dim = screen.getmaxyx() + if key in [curses.KEY_DOWN, curses.KEY_RIGHT, curses.KEY_UP, curses.KEY_LEFT]: + explore.displace(explore.interpretkey(key),(floor(dim[0]/2),floor(dim[1]/4))) + if key in [ord('.'), ord('/')]: + explore.rockmod((floor(dim[0]/2), floor(dim[1]/4)), key == ord('/')) + if key in [ord('s'), ord('l')]: + screen.addstr(dim[0]-1, 0, 'File:'+' '*(dim[1]-6)) + curses.echo() + file = screen.getstr(dim[0]-1, 6) + curses.noecho() + if key == ord('s'): + explore.save(file) + else: + screen.clear() + explore.load(file) + if key == ord('\\'): + explore.setportal((floor(dim[0]/2), floor(dim[1]/4))) + screen.addstr(0, 0, explore.get_area(dim)) + screen.addstr(dim[0]-1, 0, str(explore.getinv())) + screen.refresh() + key = screen.getch(floor(dim[0]/2), 2*floor(dim[1]/4)) + +screen.scrollok(True) +curses.nocbreak() +screen.keypad(False) +curses.echo() +curses.endwin() #finished program; resets to normal diff --git a/sorter.py b/sorter.py new file mode 100644 index 0000000..ced8d7f --- /dev/null +++ b/sorter.py @@ -0,0 +1,96 @@ +from math import floor + +class Sorter: + def __init__(self): + self.data = [] + + def __len__(self): + return len(self.data) + + def comp(self, item1, item2): #these should be dim's + #will return 'e' for equal, 'l' for less (i.e. item1 < item2), and 'g' for greater + if item1[0] < item2[0]: + return 'l' + if item1[0] == item2[0] and item1[1] < item2[1]: + return 'l' + if item1[0] == item2[0] and item1[1] == item2[1]: + return 'e' + if item1[0] == item2[0] and item1[1] > item2[1]: + return 'g' + if item1[0] > item2[0]: + return 'g' + + def __contains__(self, item): + if len(self) == 0: #messes up the code if length == 0 + return False + lo = 0 + hi = len(self) - 1 + while hi > lo: + mid = floor((hi-lo)/2)+lo + dif = self.comp(self[mid],item) + if dif == 'l': + hi = mid-1 + if dif == 'g': + lo = mid+1 + if dif == 'e': + return True + if self[lo] == item: + return True + return False + + def __getitem__(self, ind): + return self.data[ind] + + def insert(self, item): + if len(self) == 0: + self.data.insert(0, item) + return 0 + lo = 0 + hi = len(self) - 1 + while hi > lo: + mid = floor((hi-lo)/2)+lo + dif = self.comp(self[mid],item) + if dif == 'l': + hi = mid-1 + if dif == 'g': + lo = mid+1 + if dif == 'e': + self.data.insert(mid, item) + return mid+1 + dif = self.comp(self[lo],item) + if dif == 'l': + self.data.insert(lo,item) + return lo + if dif == 'e' or dif == 'g': + self.data.insert(lo+1,item) + return lo+1 + + def index(self, item): + lo = 0 + hi = len(self) - 1 + while hi > lo: + mid = floor((hi-lo)/2)+lo + dif = self.comp(self[mid],item) + if dif == 'l': + hi = mid-1 + if dif == 'g': + lo = mid+1 + if dif == 'e': + return mid + if item == self[lo]: + return lo + else: + raise(IndexError) + + def remove(self, item): + ind = self.index(item) + self.data.pop(ind) + return ind + + def __repr__(self): + return str(self.data) +test = Sorter() +test.insert((1,2)) +test.insert((1,3)) +print(test.insert((0,2))) +print((1,4) in test) diff --git a/worlds/home b/worlds/home new file mode 100644 index 0000000..d5e131e --- /dev/null +++ b/worlds/home @@ -0,0 +1,196 @@ +247 -49 23 +454 -30 False n +451 -22 False n +447 -55 False n +442 -15 False n +440 -41 False n +437 -9 False n +436 -27 False n +434 36 False n +434 -75 False n +424 48 False n +423 21 False n +421 6 False n +420 22 False n +417 12 False n +412 46 False n +412 -68 False n +406 -17 False n +403 54 False n +397 53 False n +395 -83 False n +392 33 False n +392 1 False n +390 56 False n +389 -89 False n +388 58 False n +388 40 False n +387 -31 False n +387 -66 False n +383 57 False n +382 -44 False n +381 14 False n +366 -15 False n +359 23 False n +356 -6 False n +354 41 False n +351 -26 False n +349 -7 False n +344 20 False n +344 -25 False n +342 11 False n +341 52 False n +334 11 False n +334 4 False n +325 50 False n +320 -12 True p +319 48 False n +316 7 False n +316 -11 True n +316 -15 True n +315 -11 True n +315 -13 True n +315 -15 True n +314 -11 True n +314 -12 True n +314 -13 True n +314 -14 True n +314 -15 True n +313 2 False n +312 -11 True n +312 -12 True n +312 -13 True n +312 -14 True n +312 -15 True n +311 -11 False n +311 -12 False n +311 -13 False n +311 -14 True n +311 -15 False n +310 -13 True n +310 -14 False n +310 -15 False n +309 -13 False n +309 -14 True n +308 31 False n +308 -11 True n +308 -12 True n +308 -13 True n +308 -14 True n +308 -15 True n +307 -7 False n +307 -8 False n +307 -9 True p +307 -19 True p +306 -11 True n +306 -12 True n +306 -13 True n +306 -14 True n +306 -15 True n +305 40 False n +305 -11 True n +305 -15 True n +304 -11 True n +304 -12 False n +304 -13 False n +304 -14 False n +304 -15 True n +303 -7 False n +303 -11 True n +303 -12 True n +303 -13 True n +303 -14 True n +303 -15 True n +302 -11 False n +302 -12 False n +302 -13 False n +302 -14 False n +302 -15 False n +301 -11 True n +301 -12 True n +301 -13 True n +301 -14 True n +301 -15 True n +300 27 False n +300 -11 False n +300 -12 False n +300 -13 True n +300 -14 False n +300 -15 False n +299 -13 True n +298 -11 True n +298 -12 True n +298 -13 True n +298 -14 True n +298 -15 True n +296 22 False n +293 21 False n +293 17 False n +293 7 False n +293 -13 True p +278 13 False n +277 5 False n +276 22 False n +270 13 False n +268 11 False n +262 4 False n +252 -40 False n +252 -48 False n +248 4 False n +247 -23 False n +246 -19 False n +245 -47 False n +244 -5 False n +244 -16 False n +244 -36 False n +240 26 False n +238 3 False n +237 8 False n +230 29 False n +213 29 False n +213 28 False n +213 18 False n +213 -5 False n +211 -22 False n +211 -36 False n +209 -25 False n +203 -30 False n +186 -28 False n +178 -27 False n +167 6 False n +162 -41 False n +159 -12 False n +153 -15 False n +145 1 False n +135 -51 False n +126 -20 False n +123 4 False n +109 56 False n +104 47 False n +100 6 False n +97 40 False n +96 10 False n +89 48 False n +86 8 False n +85 14 False n +82 48 False n +77 -25 False n +74 43 False n +74 14 False n +71 -24 False n +70 59 False n +67 2 False n +66 -43 False n +64 -39 False n +57 -43 False n +56 38 False n +52 55 False n +49 45 False n +46 6 False n +43 -12 False n +______ +None +320 -12 293 -13 +307 -9 307 -19 +307 -19 307 -9 +293 -13 320 -12 |