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:
		
							
								
								
									
										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
 | 
				
			||||||
		Reference in New Issue
	
	Block a user