aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--README.md30
-rw-r--r--explore.py97
-rw-r--r--inv.py1
-rw-r--r--isportal.py56
-rw-r--r--isrock.py55
-rw-r--r--rocks.py41
-rw-r--r--sorter.py96
-rw-r--r--worlds/home196
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)
diff --git a/inv.py b/inv.py
new file mode 100644
index 0000000..8d97b59
--- /dev/null
+++ b/inv.py
@@ -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