├── 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 |
22 | 23 | |Folder/File|Description| 24 | |:--:|:--:| 25 | |maps/valid/| Maps that your project should accept | 26 | |maps/invalid/| Maps that your project should not accept (used by the py script) | 27 | |maps/generated/| Generated maps after running the python script | 28 | |generator.py| The python script to automate maps generation | 29 | |tester.sh| The shell script used to fetch the output from your project | 30 | 31 |
32 | 33 | ## Next steps 34 | **1.** Fork/download the code from the repository to any path of your choice in your computer (the best would be to sit right next to your **so_long** folder, like described below) 35 | 36 | **2.** If your directory tree looks like this... 37 | 38 | ```txt 39 | │ 40 | ├── so_long_map_validator (this directory) 41 | ├── so_long (your repository) 42 | │ 43 | ... 44 | ``` 45 | then you can skip this step. Otherwise, you must change the **PROJ** variable on the **Makefile**. It should look something like this: 46 | 47 | ```Makefile 48 | #_/=\_/=\_/=\_/=\_/=\_/=\_/=\_/=\_/=\_/=\_ FOLDERS _/=\_/=\_/=\_/=\_/=\_/=\_/=\_/=\_/=\_/=\_ 49 | PROJ = ../so_long 50 | ``` 51 | 52 | **3.** If you reached this step, you can finally execute it! This project uses a mix of shell and python scripts, so there are no compilations. Also, the Makefile was purposedly developed to easy the management of this programs, so this Makefile will compile yours and execute the tests. 53 | 54 | To compile the mandatory part, your executable must be named as `so_long` 55 | 56 | ```shell 57 | make 58 | ``` 59 | 60 | If you need to compile your bonus you must have your executable named as `so_long_bonus` and run the following command: 61 | 62 | ```shell 63 | make bonus 64 | ``` 65 | 66 | To generate a new map, use: 67 | 68 | ```shell 69 | make gen 70 | ``` 71 | 72 | To clean all the generated maps so far, use: 73 | 74 | ```shell 75 | make cleangen 76 | ``` 77 | 78 | The `fclean` and `clean` rules will clean your project object files. 79 | 80 | The Makefile was configured to pull updates from the repository, so its ok if it is taking a while to start. After looking for updates, the tool should display something like this: 81 | 82 |
83 | 84 | | Correct Output | 85 | |:--:| 86 | |![Correct Output](https://user-images.githubusercontent.com/93390807/217015792-6d5bdd6f-4ca6-4e7e-9a4c-9761deb0e802.png)| 87 | 88 |
89 | 90 | or this, if any misconfiguration of any kind happened: 91 |
92 | 93 | | Error Output | 94 | |:--:| 95 | |![Error Output](https://user-images.githubusercontent.com/93390807/217015797-c3455a92-69be-45de-9024-8cb896279614.png)| 96 | 97 |
98 | 99 | ## Generating maps 100 | 101 | Alongside with the shell script, there is a generator.py file that can be used to generate a new map, valid or invalid (that's random for now) with given height and width specified by input. 102 | 103 | The generated map will be placed inside the [maps/generated](/maps/generated) folder. 104 | 105 | A glimpse on how the script should behave 106 | 107 |
108 | 109 | | Map Generation Script | 110 | |:--:| 111 | |![Map Generation Script](https://user-images.githubusercontent.com/93390807/217015799-e3aabe37-f8ea-4b21-9e94-7496a4825c8c.png)| 112 | 113 |
114 | 115 | ## Author 116 | 117 | Nuno Jesus, 42 Porto, ncarvalh 118 | 119 | --------------------------------------------------------------------------------