Merge pull request 'saving and loading map parser prototype' (#4) from prototype_saves into master
Reviewed-on: #4 Reviewed-by: BrokenBrad <eddyjiofak@gmail.com> Reviewed-by: Mat_02 <diletomatteo@gmail.com>
This commit is contained in:
commit
dfccb65f02
35
prototypes/saves_prototypes/README
Normal file
35
prototypes/saves_prototypes/README
Normal file
@ -0,0 +1,35 @@
|
||||
# Prototypes Saves files
|
||||
|
||||
This prototype is aiming at defining a structure for saving "map" files.
|
||||
|
||||
The objective is to have it quite modular so that if we want to add more level we can easily do so.
|
||||
|
||||
Another objective is that theses files are as small as possible without missing information.
|
||||
|
||||
## What needs to be saved
|
||||
|
||||
We need to have the shape of the map itself and the shape and number of each pieces it is a minimum
|
||||
|
||||
I also want to define an header (and maybe a footer) to the file so that it is easier to recover if any corruption occure to a drive
|
||||
(we all been trough that)
|
||||
|
||||
## Method
|
||||
|
||||
I would like to save a file as byte so I need to have a bitewise parser. This is the objective of this prototype
|
||||
|
||||
## Structure
|
||||
|
||||
The map file should have the following structure:
|
||||
- The file should be named <map_name>.shmap (where shmap stand for shape map)
|
||||
- The file should start with the ascii characters S, M then S (0x534D53)
|
||||
- The file should end with the ascii characters S, M then E (0x534D45)
|
||||
- First we define the map shape
|
||||
1) the map should always be a square. the lenght of this square will be the first octet after the header.
|
||||
2) next we have s x s (where s is the size of the square) bits (1/0) where
|
||||
- 0 represent an empty cell (where we can place a shape)
|
||||
- 1 represent a filled cell (where we can't place a shape)
|
||||
- Next we define the amount of shape for this level with a number on one octet
|
||||
- Next we define each shapes with the same method defined previously for a map
|
||||
|
||||
With all that we should have all that is needed for a level to work... further information could be added later this is just a prototype atm
|
||||
|
197
prototypes/saves_prototypes/parser.py
Executable file
197
prototypes/saves_prototypes/parser.py
Executable file
@ -0,0 +1,197 @@
|
||||
#!/bin/python
|
||||
import os
|
||||
|
||||
|
||||
class MapNotSquareException(Exception):
|
||||
"""
|
||||
Matrix used is not a Square and cannot be interpretted as a piece
|
||||
"""
|
||||
|
||||
|
||||
class PieceNotSquareException(Exception):
|
||||
"""
|
||||
Matrix used is not a Square and cannot be interpretted as a piece
|
||||
"""
|
||||
|
||||
|
||||
class SaveParser:
|
||||
"""
|
||||
Parser for the game file
|
||||
"""
|
||||
def __init__(self):
|
||||
self.map_shape = [[0]]
|
||||
self.pieces = []
|
||||
|
||||
def define_map(self, map_shape):
|
||||
size = len(map_shape)
|
||||
for row in map_shape:
|
||||
if size != len(row):
|
||||
raise MapNotSquareException
|
||||
self.map_shape = map_shape
|
||||
|
||||
def add_piece(self, piece_shape):
|
||||
size = len(piece_shape)
|
||||
for row in piece_shape:
|
||||
if size != len(row):
|
||||
raise PieceNotSquareException
|
||||
self.pieces.append(piece_shape)
|
||||
|
||||
def __str__(self):
|
||||
return str(self.map_shape)
|
||||
|
||||
def shape_to_bytes(self, shape_matrix):
|
||||
shape_list = [b for r in shape_matrix for b in r]
|
||||
byte_ammount = len(shape_list) // 8 + 1
|
||||
tray = 0
|
||||
for i in shape_list:
|
||||
tray = (tray << 1) | i
|
||||
return tray.to_bytes(byte_ammount, 'big')
|
||||
|
||||
def bytes_to_shape(self, bytes_list, map_size):
|
||||
list_octet = []
|
||||
for octet in bytes_list:
|
||||
octet_data = list(f"{octet:08b}")
|
||||
[list_octet.append(d) for d in octet_data]
|
||||
list_octet = list_octet[-(map_size**2):]
|
||||
|
||||
matrix = []
|
||||
for i in range(map_size):
|
||||
matrix.append([])
|
||||
for j in range(map_size):
|
||||
matrix[i].append(list_octet.pop(0))
|
||||
return matrix
|
||||
|
||||
def load(self, filename):
|
||||
"""
|
||||
load the file and prepare to parse informations
|
||||
"""
|
||||
with open(filename, mode='br') as file:
|
||||
data = list(file.read())
|
||||
|
||||
data_pos = [0, 0]
|
||||
for i in range(len(data)):
|
||||
if data[i] == 83 and data[i+1] == 77 and data[i+2] == 83: # SMS
|
||||
data_pos[0] = i+3
|
||||
break
|
||||
for i in range(data_pos[0], len(data)):
|
||||
if data[i] == 83 and data[i+1] == 77 and data[i+2] == 69: # SME
|
||||
data_pos[1] = i
|
||||
break
|
||||
map_data = data[data_pos[0]:data_pos[1]]
|
||||
self.define_map(self.bytes_to_shape(map_data[1:((map_data[0]**2)//8)+2], map_data[0]))
|
||||
map_data = map_data[(map_data[0]**2) // 8 + 2:]
|
||||
pieces_ammount = map_data.pop(0)
|
||||
for i in range(pieces_ammount):
|
||||
print(map_data)
|
||||
piece_size = map_data.pop(0)
|
||||
print(piece_size)
|
||||
piece_data = map_data[:(piece_size**2) // 8 + 1]
|
||||
print(piece_data)
|
||||
map_data = map_data[(map_data[0]**2) // 8 + 2:]
|
||||
print(map_data)
|
||||
self.add_piece(self.bytes_to_shape(piece_data, piece_size))
|
||||
|
||||
def save(self, filename):
|
||||
"""
|
||||
save parsed info to the file
|
||||
"""
|
||||
save_data = b''
|
||||
save_data += b'SMS'
|
||||
save_data += len(self.map_shape).to_bytes(1, 'big')
|
||||
save_data += self.shape_to_bytes(self.map_shape)
|
||||
save_data += len(self.pieces).to_bytes(1, 'big')
|
||||
for piece in self.pieces:
|
||||
save_data += len(piece).to_bytes(1, 'big')
|
||||
save_data += self.shape_to_bytes(piece)
|
||||
save_data += b'SME'
|
||||
with open(filename, mode='bw') as file:
|
||||
file.write(save_data)
|
||||
|
||||
|
||||
def show_matrix(matrix, highlight: tuple = None):
|
||||
"""
|
||||
:matrix: matrix to draw
|
||||
:highlight: tuple of the coordinates to hightligh
|
||||
"""
|
||||
size = len(matrix)
|
||||
h_x, h_y = None, None
|
||||
if highlight:
|
||||
h_x, h_y = highlight
|
||||
if size != len(matrix[0]):
|
||||
print("ERROR: The matrix is not square")
|
||||
return
|
||||
|
||||
line_str = "+" + ''.join(['-+' for _ in range(size)])
|
||||
|
||||
for k, x in enumerate(matrix):
|
||||
print(line_str)
|
||||
print('|', end="")
|
||||
for l, y in enumerate(x):
|
||||
if k == h_x and l == h_y:
|
||||
print("\033[42m", end="")
|
||||
print(str(y) + "\033[00m" + '|', end="")
|
||||
print()
|
||||
print(line_str)
|
||||
|
||||
|
||||
def cls():
|
||||
'clear the screen'
|
||||
for _ in range(os.get_terminal_size()[1]):
|
||||
print()
|
||||
|
||||
|
||||
def menu(P):
|
||||
"""draw a simple menu to test the SaveParser class"""
|
||||
|
||||
print("1) define terrain")
|
||||
print("2) add a piece")
|
||||
print("3) show data")
|
||||
print("4) save data")
|
||||
print("5) load data")
|
||||
print("6) quit")
|
||||
item = int(input("Select an option :"))
|
||||
cls()
|
||||
match item:
|
||||
case 1:
|
||||
size = int(input("what is the size of the map: "))
|
||||
P.map_shape = [[0 for _ in range(size)] for _ in range(size)]
|
||||
for i in range(size):
|
||||
for j in range(size):
|
||||
cls()
|
||||
show_matrix(P.map_shape, (i,j))
|
||||
P.map_shape[i][j] = int(input("0: empty; 1: filled :"))
|
||||
case 2:
|
||||
size = int(input("what is the size of the piece: "))
|
||||
temp = [[0 for _ in range(size)] for _ in range(size)]
|
||||
for i in range(size):
|
||||
for j in range(size):
|
||||
cls()
|
||||
show_matrix(temp, (i,j))
|
||||
temp[i][j] = int(input("0: empty; 1: filled :"))
|
||||
P.add_piece(temp)
|
||||
case 3:
|
||||
if P.map_shape:
|
||||
print("map:")
|
||||
show_matrix(P.map_shape)
|
||||
for i, v in enumerate(P.pieces):
|
||||
print()
|
||||
print(f"piece {i+1}")
|
||||
show_matrix(v)
|
||||
case 4:
|
||||
filename = input('enter the file name (default: default.smap):')
|
||||
filename = filename if filename else "default.smap"
|
||||
P.save(filename)
|
||||
case 5:
|
||||
filename = input('enter the file name (default: default.smap):')
|
||||
filename = filename if filename else "default.smap"
|
||||
P.load(filename)
|
||||
case 6:
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
cls()
|
||||
P = SaveParser()
|
||||
while menu(P):
|
||||
pass
|
Loading…
Reference in New Issue
Block a user