├── maps ├── invalid │ ├── empty_file.ber │ ├── one_character.ber │ ├── no_corner_walls.ber │ ├── no_outter_walls.ber │ ├── no_player.ber │ ├── duplicate_exit.ber │ ├── no_coins.ber │ ├── no_exit.ber │ ├── no_coins_path.ber │ ├── no_exit_path.ber │ ├── duplicate_player.ber │ ├── non_rectangular.ber │ ├── invalid_entities.ber │ ├── empty_line_at_start.ber │ └── empty_line_in_between.ber └── valid │ ├── minimal_horizontal.ber │ ├── minimal_vertical.ber │ ├── valid1.ber │ ├── valid2.ber │ ├── valid3.ber │ ├── valid4.ber │ └── valid5.ber ├── .gitignore ├── Makefile ├── srcs ├── tester.sh └── generator.py └── README.md /maps/invalid/empty_file.ber: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /maps/invalid/one_character.ber: -------------------------------------------------------------------------------- 1 | 1 -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | output.log 2 | temp 3 | -------------------------------------------------------------------------------- /maps/valid/minimal_horizontal.ber: -------------------------------------------------------------------------------- 1 | 11111 2 | 1PEC1 3 | 11111 -------------------------------------------------------------------------------- /maps/valid/minimal_vertical.ber: -------------------------------------------------------------------------------- 1 | 111 2 | 1P1 3 | 1E1 4 | 1C1 5 | 111 -------------------------------------------------------------------------------- /maps/invalid/no_corner_walls.ber: -------------------------------------------------------------------------------- 1 | 01111111110 2 | 1CC0000E0P1 3 | 01111111110 -------------------------------------------------------------------------------- /maps/invalid/no_outter_walls.ber: -------------------------------------------------------------------------------- 1 | 00000000000 2 | 0CC1111E1P0 3 | 00000000000 -------------------------------------------------------------------------------- /maps/invalid/no_player.ber: -------------------------------------------------------------------------------- 1 | 11111111111 2 | 1000000E101 3 | 1C1C1000111 4 | 11111111111 -------------------------------------------------------------------------------- /maps/invalid/duplicate_exit.ber: -------------------------------------------------------------------------------- 1 | 111111 2 | 1E0001 3 | 1C0001 4 | 1000P1 5 | 1000E1 6 | 111111 -------------------------------------------------------------------------------- /maps/invalid/no_coins.ber: -------------------------------------------------------------------------------- 1 | 11111111111 2 | 1P000000011 3 | 10110010101 4 | 1E110001111 5 | 11111111111 -------------------------------------------------------------------------------- /maps/invalid/no_exit.ber: -------------------------------------------------------------------------------- 1 | 11111111111 2 | 1P000000011 3 | 101100C0111 4 | 101C0000CC1 5 | 11111111111 -------------------------------------------------------------------------------- /maps/invalid/no_coins_path.ber: -------------------------------------------------------------------------------- 1 | 11111111111 2 | 1P000000011 3 | 101100C0101 4 | 1E1C0001CC1 5 | 11111111111 -------------------------------------------------------------------------------- /maps/invalid/no_exit_path.ber: -------------------------------------------------------------------------------- 1 | 11111111111 2 | 1P000000011 3 | 101100C0101 4 | 1C1C00010E1 5 | 11111111111 -------------------------------------------------------------------------------- /maps/valid/valid1.ber: -------------------------------------------------------------------------------- 1 | 1111111111111 2 | 10010000000C1 3 | 1000011111001 4 | 1P0011E000001 5 | 1111111111111 -------------------------------------------------------------------------------- /maps/invalid/duplicate_player.ber: -------------------------------------------------------------------------------- 1 | 111111111111 2 | 1E00000000P1 3 | 10000P000001 4 | 1C0000000001 5 | 111111111111 -------------------------------------------------------------------------------- /maps/invalid/non_rectangular.ber: -------------------------------------------------------------------------------- 1 | 1111111111111111 2 | 10000P0000000111111 3 | 10001000C00001 4 | 10001100100001111111 5 | 1000E11C1C0001111 6 | 11111111111111 7 | -------------------------------------------------------------------------------- /maps/valid/valid2.ber: -------------------------------------------------------------------------------- 1 | 1111111111111111111111111111111111 2 | 1E0000000000000C00000C000000000001 3 | 1010010100100000101001000000010101 4 | 1010010010101010001001000000010101 5 | 1P0000000C00C0000000000000000000C1 6 | 1111111111111111111111111111111111 -------------------------------------------------------------------------------- /maps/invalid/invalid_entities.ber: -------------------------------------------------------------------------------- 1 | 11111111111111111111111111111 2 | 1000E000000000000000000000001 3 | 10000000000000000000000000001 4 | 1P000000000000000000000000001 5 | 1000C000000000000000000000001 6 | 10000000000000000000000000001 7 | 100000000000000000000000000c1 8 | 11111111111111111111111111111 -------------------------------------------------------------------------------- /maps/invalid/empty_line_at_start.ber: -------------------------------------------------------------------------------- 1 | 2 | 11111111111111111111111111111 3 | 1000E000000000000000000000001 4 | 10000000000000000000000000001 5 | 1P000000000000000000000000001 6 | 1000C000000000000000000000001 7 | 10000000000000000000000000001 8 | 10000000000000000000000000001 9 | 11111111111111111111111111111 -------------------------------------------------------------------------------- /maps/invalid/empty_line_in_between.ber: -------------------------------------------------------------------------------- 1 | 11111111111111111111111111111 2 | 1000E000000000000000000000001 3 | 10000000000000000000000000001 4 | 1P000000000000000000000000001 5 | 1000C000000000000000000000001 6 | 7 | 10000000000000000000000000001 8 | 10000000000000000000000000001 9 | 11111111111111111111111111111 -------------------------------------------------------------------------------- /maps/valid/valid3.ber: -------------------------------------------------------------------------------- 1 | 1111111111111111111111 2 | 1C000000000000000000P1 3 | 1011111111111111111111 4 | 101C0000000000000000C1 5 | 1010111111111111111101 6 | 10101E000000000000C101 7 | 1010111111111111110101 8 | 101C00000000000000C101 9 | 1011111111111111111101 10 | 1C000000000000000000C1 11 | 1111111111111111111111 -------------------------------------------------------------------------------- /maps/valid/valid4.ber: -------------------------------------------------------------------------------- 1 | 111111111111111111111111111111111111111 2 | 1000000000000000001E1000000000000000001 3 | 101111011110111100101001111011110111101 4 | 101111C1111011110010100111101111C111101 5 | 10111101111C111100101001111C11110111101 6 | 101111011110111100101001111011110111101 7 | 100000000000000000000000000000000000001 8 | 100000000000000000000000000000000000001 9 | 100000000000000000000000000000000000001 10 | 101111011110111100101001111011110111101 11 | 10111101111C111100101001111C11110111101 12 | 101111C1111011110010100111101111C111101 13 | 101111011110111100101001111011110111101 14 | 1000000000000000001P1000000000000000001 15 | 111111111111111111111111111111111111111 -------------------------------------------------------------------------------- /maps/valid/valid5.ber: -------------------------------------------------------------------------------- 1 | 1111111111111111111111111111 2 | 1C000000000001100000000000C1 3 | 1011110111110110111110111101 4 | 101111C11111011011111C111101 5 | 1011110111110110111110111101 6 | 1000000000000000000000000001 7 | 1011110110111111110110111101 8 | 1011110110111111110110111101 9 | 1000000110000110000110000001 10 | 1000000110000110000110000001 11 | 1011110110000000000110111101 12 | 1011110110111001110110111101 13 | 1000000110100000010110000001 14 | 10000001101E0000P10110000001 15 | 1011110110111111110110111101 16 | 1011110110000000000110111101 17 | 1000000110000110000110000001 18 | 1000000110000110000110000001 19 | 1011110110111111110110111101 20 | 1011110110111111110110111101 21 | 1000000000000000000000000001 22 | 1011110111110110111110111101 23 | 101111C11111011011111C111101 24 | 1011110111110110111110111101 25 | 1C000000000001100000000000C1 26 | 1111111111111111111111111111 -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | #_/=\_/=\_/=\_/=\_/=\_/=\_/=\_/=\_/=\_/=\_ COLORS _/=\_/=\_/=\_/=\_/=\_/=\_/=\_/=\_/=\_/=\_ 2 | RESET = \033[0m 3 | BLACK = \033[1;30m 4 | RED = \033[1;31m 5 | GREEN = \033[1;32m 6 | YELLOW = \033[1;33m 7 | BLUE = \033[1;34m 8 | PURPLE = \033[1;35m 9 | CYAN = \033[1;36m 10 | WHITE = \033[1;37m 11 | 12 | #_/=\_/=\_/=\_/=\_/=\_/=\_/=\_/=\_/=\_/=\_ COMMANDS _/=\_/=\_/=\_/=\_/=\_/=\_/=\_/=\_/=\_/=\_ 13 | PY = python3 -B 14 | 15 | #_/=\_/=\_/=\_/=\_/=\_/=\_/=\_/=\_/=\_/=\_ FLAGS _/=\_/=\_/=\_/=\_/=\_/=\_/=\_/=\_/=\_/=\_ 16 | MKFLAGS = --no-print-directory 17 | 18 | #_/=\_/=\_/=\_/=\_/=\_/=\_/=\_/=\_/=\_/=\_ FOLDERS _/=\_/=\_/=\_/=\_/=\_/=\_/=\_/=\_/=\_/=\_ 19 | PROJ = ../so_long 20 | GEN = maps/generated 21 | SRCS = srcs 22 | 23 | #_/=\_/=\_/=\_/=\_/=\_/=\_/=\_/=\_/=\_/=\_ RULES _/=\_/=\_/=\_/=\_/=\_/=\_/=\_/=\_/=\_/=\_ 24 | 25 | all: pull setup 26 | 27 | pull: 28 | echo "[$(CYAN) Git $(RESET)] Checking for updates..." 29 | git pull origin main 30 | 31 | setup: 32 | sh $(SRCS)/tester.sh $(PROJ) all 33 | 34 | bonus: pull setup_bonus 35 | 36 | setup_bonus: 37 | sh $(SRCS)/tester.sh $(PROJ) bonus 38 | 39 | clean: 40 | make clean $(MKFLAGS) -C $(PROJ) 41 | 42 | fclean: 43 | make fclean $(MKFLAGS) -C $(PROJ) 44 | 45 | $(GEN): 46 | mkdir -p maps/generated 47 | 48 | gen: $(GEN) 49 | $(PY) $(SRCS)/generator.py 50 | 51 | cleangen: 52 | rm -rf maps/generated 53 | 54 | .SILENT: 55 | re: fclean all 56 | -------------------------------------------------------------------------------- /srcs/tester.sh: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | 3 | RESET="\033[0m" 4 | BLACK="\033[1;30m" 5 | RED="\033[1;31m" 6 | GREEN="\033[1;32m" 7 | YELLOW="\033[1;33m" 8 | BLUE="\033[1;34m" 9 | PURPLE="\033[1;35m" 10 | CYAN="\033[1;36m" 11 | WHITE="\033[1;37m" 12 | 13 | LOGFILE="output.log" 14 | TEMPFILE="temp" 15 | INVALID_MAPS="maps/invalid" 16 | 17 | EXEC="so_long" 18 | EXEC_BONUS="so_long_bonus" 19 | 20 | # $S1 = path to the project directory 21 | # $S2 = makefile rule to run 22 | 23 | log_title() 24 | { 25 | echo "\t/==\-/==\-/==\-/==\-/==\-/==\-/==\-/==\-/==\-/==\-/==\-/==\-/==\\" >> $LOGFILE 26 | echo "\t|==| |==|" >> $LOGFILE 27 | echo "\t|==| output.log |==|" >> $LOGFILE 28 | echo "\t|==| |==|" >> $LOGFILE 29 | echo "\t\==/-\==/-\==/-\==/-\==/-\==/-\==/-\==/-\==/-\==/-\==/-\==/-\==/" >> $LOGFILE 30 | } 31 | 32 | log_terminal() 33 | { 34 | # If the Error string was found, output is not empty 35 | if [ "$output" ]; then 36 | echo -n "$i: $GREEN"OK"$RESET " 37 | else 38 | echo -n "$i: $RED"KO"$RESET " 39 | fi 40 | } 41 | 42 | log_file() 43 | { 44 | # If the Error string was found, output is not empty 45 | if [ "$output" ]; then 46 | echo "\n[$WHITE#$i$RESET][$GREEN"SUCCESS"$RESET] $CYAN$test_file$RESET\n" >> $LOGFILE 47 | else 48 | echo "\n[$WHITE#$i$RESET][$RED"FAILURE"$RESET] $CYAN$test_file$RESET\n" >> $LOGFILE 49 | fi 50 | valgrind $path/$program $INVALID_MAPS/$test_file >> $LOGFILE 2>&1 51 | echo "\n\t------------------------------------------------------------" >> $LOGFILE 52 | } 53 | 54 | compile() 55 | { 56 | # Remove any previous generated output files 57 | rm -rf $LOGFILE 58 | 59 | # Check for the existence of the Makefile 60 | if [ ! -f $path/Makefile ]; then 61 | echo "[$RED"Error"$RESET] No Makefile was found in the \"$path\" directory" 62 | exit 0 63 | fi 64 | 65 | # Start compilation 66 | echo "[$CYAN"Compilation"$RESET] Compiling your project..." 67 | make fclean -C $path 1> /dev/null 68 | make $rule -C $path 1> /dev/null 69 | 70 | # Checks the return value of the make command to assert a successful compilation 71 | if [ $? -ne 0 ]; then 72 | echo "[$RED"Error"$RESET] Compilation failed" 73 | exit 0 74 | fi 75 | 76 | echo "[$CYAN"Compilation"$RESET] $GREEN"Success"$RESET" 77 | } 78 | 79 | execute() 80 | { 81 | log_title 82 | 83 | i=1 84 | for test_file in $(ls $INVALID_MAPS) 85 | do 86 | $path/$program $INVALID_MAPS/$test_file > $TEMPFILE 2>&1 87 | 88 | output="$(grep Error $TEMPFILE)" 89 | log_terminal 90 | log_file 91 | 92 | i=$(expr $i + 1) 93 | done 94 | 95 | rm -rf $TEMPFILE 96 | echo "\n\n\t--- Use $RED"cat"$RESET $CYAN"$LOGFILE"$RESET for detailed information ---\n" 97 | 98 | } 99 | 100 | main() 101 | { 102 | path=$1 103 | rule=$2 104 | 105 | compile 106 | if [ "$rule" = "bonus" ]; then 107 | program=$EXEC_BONUS 108 | else 109 | program=$EXEC 110 | fi 111 | execute 112 | } 113 | 114 | main $1 $2 -------------------------------------------------------------------------------- /srcs/generator.py: -------------------------------------------------------------------------------- 1 | import random 2 | import math 3 | import os 4 | from colorama import Style 5 | from colorama import Fore 6 | 7 | #! Colors 8 | RESET = Style.RESET_ALL 9 | LRED = Fore.LIGHTRED_EX 10 | LYELLOW = Fore.LIGHTYELLOW_EX 11 | LBLUE = Fore.LIGHTBLUE_EX 12 | LGREEN = Fore.LIGHTGREEN_EX 13 | 14 | #! Other macros 15 | GEN_PATH = "maps/generated" 16 | WALL_PROBABILITY = 0.80 17 | COIN_PROBABILITY = 0.80 18 | 19 | #! Entities 20 | WALL = '1' 21 | PLAYER = 'P' 22 | COIN = 'C' 23 | EXIT = 'E' 24 | EMPTY = '0' 25 | 26 | def generate_coins(contents, x, y) -> None: 27 | num_coins = random.randint(0, math.floor((max(x, y) * COIN_PROBABILITY))) 28 | print(f"Num coins: {num_coins}") 29 | 30 | for i in range(0, num_coins): 31 | cx, cy = random.randint(1, x - 2), random.randint(1, y - 2) 32 | 33 | if contents[cy][cx] == EMPTY: 34 | contents[cy][cx] = COIN 35 | 36 | def generate_player_and_exit(contents, x, y) -> None: 37 | px, py = random.randint(1, x - 2), random.randint(1, y - 2) 38 | 39 | #print(f"Player (X/Y): {[px, py]}") 40 | ex, ey = random.randint(1, x - 2), random.randint(1, y - 2) 41 | 42 | #print(f"Exit (X/Y): {[ex, ey]}") 43 | contents[py][px] = 'P' 44 | contents[ey][ex] = 'E' 45 | 46 | def get_random_unvisited_node(visited, x, y): 47 | 48 | unvisited_nodes = [] 49 | 50 | if not visited[y - 1][x]: 51 | unvisited_nodes.append([x, y - 1]) 52 | if not visited[y + 1][x]: 53 | unvisited_nodes.append([x, y + 1]) 54 | if not visited[y][x - 1]: 55 | unvisited_nodes.append([x - 1, y]) 56 | if not visited[y][x + 1]: 57 | unvisited_nodes.append([x + 1, y]) 58 | if len(unvisited_nodes) < 2: 59 | return None 60 | return unvisited_nodes[random.randint(0, len(unvisited_nodes) - 1)] 61 | 62 | def randomized_dfs(contents, x, y, visited): 63 | visited[y][x] = True 64 | contents[y][x] = EMPTY 65 | pos = get_random_unvisited_node(visited, x, y) 66 | 67 | while pos != None: 68 | randomized_dfs(contents, pos[0], pos[1], visited) 69 | pos = get_random_unvisited_node(visited, x, y) 70 | 71 | def generate_walls(contents, x, y): 72 | visited = [] 73 | visited.append([True for _ in range (0, x)]) 74 | for _ in range (0, y - 2): 75 | visited.append([True] + [False for _ in range(0, x - 2)] + [True]) 76 | visited.append([True for _ in range (0, x)]) 77 | 78 | # print(visited) 79 | start_x, start_y = random.randint(1, x - 2), random.randint(1, y - 2) 80 | randomized_dfs(contents, start_x, start_y, visited) 81 | 82 | 83 | def generate_map(filename, x, y) -> None: 84 | contents = [[WALL for _ in range(0, x)] for _ in range(0, y)] 85 | 86 | generate_walls(contents, x, y) 87 | generate_coins(contents, x, y) 88 | generate_player_and_exit(contents, x, y) 89 | 90 | f = open(filename, "x") 91 | for line in contents: 92 | f.write("".join(line) + '\n') 93 | for char in line: 94 | if char is WALL: 95 | print(f'{LBLUE}{char}{RESET}', end = "") 96 | elif char is COIN: 97 | print(f'{LYELLOW}{char}{RESET}', end = "") 98 | elif char is EXIT: 99 | print(f'{LGREEN}{char}{RESET}', end = "") 100 | elif char is PLAYER: 101 | print(f'{LRED}{char}{RESET}', end = "") 102 | else: 103 | print(char, end = "") 104 | print() 105 | f.close() 106 | print(f'\n\t --- The map was saved in {LRED}{filename}{RESET}. ---\n') 107 | 108 | if __name__ == "__main__" : 109 | n = 1 110 | x, y = -1, -1 111 | 112 | while y < 3 or x < 3: 113 | try: 114 | x = int(input(f'Width: ')) 115 | y = int(input(f'Height: ')) 116 | except ValueError: 117 | print(f'\n\t===== {LRED}INPUT MUST BE BIGGER THAN 2{RESET} =====\n') 118 | 119 | while os.path.exists(f'{GEN_PATH}/map{n}.ber'): 120 | n += 1 121 | 122 | random.seed() 123 | generate_map(f'{GEN_PATH}/map{n}.ber', x, y) 124 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Description 2 | 3 | This tool was developed to help **42** students, specifically in **so_long**. At the time of the first launch (February 6th 2023), the tool was built using the most up-to-date version of the subject found so far on intra. 4 | 5 | This tool is currently **ONLY** performing tests to ensure your project does not support wrongly formatted maps. Make sure you check the [**releases**](https://github.com/Nuno-Jesus/so_long_map_validator/releases) section to keep-up-to date on any new changes. 6 | 7 | ## This is great, you mind if I use your code? 8 | 9 | **You are free to fork the repository and study/modify/test the code on your own**. However, I would really appreciate it if you could text me on Slack (**ncarvalh**) whenever you find a bug or you have a suggestion, so I can properly introduce hotfixes or deploy some quality-of-life patches that can benefit the 42 community in general. 10 | 11 | ## Are there any pre-requirements? 12 | > Make sure your so_long project has a functional Makefile with all the mandatory rules. 13 | 14 | In order to correctly execute the tool, you must: 15 | - have [Python](https://www.python.org/downloads/) installed; 16 | - have either [Ubuntu](https://ubuntu.com/download) or [WSL](https://learn.microsoft.com/en-us/windows/wsl/install) on your computer; 17 | - install [pip](https://linuxize.com/post/how-to-install-pip-on-ubuntu-20.04/) **ONLY IF** there is a missing package that you might need to install manually (text me, otherwise I won't know); 18 | 19 | ## Repository contents 20 | 21 |