Skip to content

Commit 4c57a33

Browse files
authored
Merge pull request #10 from ErtyumPX/mass-production
Infrastructure for Mass Production and Brute Force Solver
2 parents e755010 + 3dec879 commit 4c57a33

9 files changed

+438
-109
lines changed

Makefile

+11-5
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@ SRC_DIR := hashi
44
TESTS_DIR := tests
55
PUZZLES_DIR := $(SRC_DIR)/puzzles
66

7-
TEST_PUZZLE = $(PUZZLES_DIR)/puzzle_2.csv
7+
SHOW_PUZZLE = $(PUZZLES_DIR)/puzzle_3.csv
8+
MANUAL_TEST_PUZZLE := $(PUZZLES_DIR)/puzzle_2.csv
89

910
REQUIREMENTS_FILE := requirements.txt
1011

@@ -15,7 +16,7 @@ VISUALISER_SCRIPT := $(SRC_DIR)/visualiser.py
1516
TEST_SCRIPT := $(TESTS_DIR)/hashi_test.py
1617

1718

18-
.PHONY: init test clean showoff
19+
.PHONY: init test clean showoff see
1920

2021

2122
init:
@@ -31,6 +32,11 @@ clean:
3132

3233

3334
showoff:
34-
# $(PY) $(FLAGS) $(VISUALISER_SCRIPT) -e $(TEST_PUZZLE) &
35-
$(PY) $(FLAGS) $(VISUALISER_SCRIPT) -s $(TEST_PUZZLE) &
36-
$(PY) $(FLAGS) $(SOLVER_SCRIPT) $(TEST_PUZZLE) &
35+
$(PY) $(FLAGS) $(VISUALISER_SCRIPT) -e $(SHOW_PUZZLE) &
36+
$(PY) $(FLAGS) $(VISUALISER_SCRIPT) -s $(SHOW_PUZZLE) &
37+
$(PY) $(FLAGS) $(SOLVER_SCRIPT) $(SHOW_PUZZLE) &
38+
39+
40+
see:
41+
$(PY) $(FLAGS) $(VISUALISER_SCRIPT) -s $(MANUAL_TEST_PUZZLE) &
42+
$(PY) $(FLAGS) $(SOLVER_SCRIPT) $(MANUAL_TEST_PUZZLE) &

hashi/arg_parser.py

+55
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
from node import Node
2+
3+
def parse_args(argv: list[str]) -> list[list[Node]]:
4+
"""
5+
Parse the system arguments and returns the grid, empty or solution according to flags.
6+
"""
7+
import os
8+
from export import import_empty_grid, import_solution_grid
9+
if len(argv) != 3:
10+
print("Usage: python3 ___.py <-e||-s> <path_to_grid_file>")
11+
return -1
12+
path = argv[2]
13+
if not os.path.isfile(path):
14+
print("ERROR: File does not exist")
15+
return -1
16+
if argv[1] == "-e":
17+
grid = import_empty_grid(path)
18+
elif argv[1] == "-s":
19+
grid = import_solution_grid(path)
20+
else:
21+
print("Usage: python3 ___.py <-e||-s> <path_to_grid_file>")
22+
return -1
23+
return grid
24+
25+
26+
def parse_args_empty(argv: list[str]) -> list[list[Node]]:
27+
"""
28+
Parse the system arguments and returns the empty grid.
29+
"""
30+
import os
31+
from export import import_empty_grid
32+
if len(argv) != 2:
33+
print(f"Usage: python3 ___.py <path>")
34+
return -1
35+
path = argv[1]
36+
if not os.path.isfile(path):
37+
print("ERROR: File does not exist")
38+
return -1
39+
grid = import_empty_grid(path)
40+
return grid
41+
42+
43+
def parse_to_path(argv: list[str]) -> str:
44+
"""
45+
Parse the system arguments and returns the path.
46+
"""
47+
import os
48+
if len(argv) != 2:
49+
print(f"Usage: python3 ___.py <path>")
50+
return -1
51+
path = argv[1]
52+
if os.path.isfile(path):
53+
print(f"ERROR: There is an existing file at '{path}'")
54+
return -1
55+
return path

hashi/cathegorise.py

+24
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
from node import Node
2+
3+
ISLAND_WEIGHT_FACTOR: float = 0.3
4+
STEP_WEIGHT_FACTOR: float = 1 - ISLAND_WEIGHT_FACTOR
5+
6+
def determine_difficulty(grid: list[list[Node]], step_count: int) -> float:
7+
"""
8+
Gets a grid and returns a difficulty rating.\n
9+
Rating is an integer between 0 and 1.
10+
"""
11+
grid_width: int = len(grid)
12+
grid_height: int = len(grid[0])
13+
island_amount: int = 0 # amount of islands
14+
total_island_count: int = 0 # total amount of bridges needed
15+
for x in range(grid_width):
16+
for y in range(grid_height):
17+
if grid[x][y].n_type == 1:
18+
island_amount += 1
19+
total_island_count += grid[x][y].i_count
20+
island_count_weight: float = total_island_count / island_amount / 8
21+
step_count_weight: float = step_count / island_amount
22+
# TODO: calculate difficulty and return
23+
24+

hashi/export.py

+1-53
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
from node import Node, direction_to_vector, is_in_grid
2+
from arg_parser import parse_args
23
import pygame
34
import os
45

@@ -226,59 +227,6 @@ def output_image(grid: list[list[Node]], path: str, cell_unit: int = 200) -> boo
226227
return True
227228

228229

229-
def parse_args(argv: list[str]) -> list[list[Node]]:
230-
"""
231-
Parse the system arguments and returns the grid, empty or solution according to flags.
232-
"""
233-
import os
234-
if len(argv) != 3:
235-
print("Usage: python3 ___.py <-e||-s> <path_to_grid_file>")
236-
return -1
237-
path = argv[2]
238-
if not os.path.isfile(path):
239-
print("ERROR: File does not exist")
240-
return -1
241-
if argv[1] == "-e":
242-
grid = import_empty_grid(path)
243-
elif argv[1] == "-s":
244-
grid = import_solution_grid(path)
245-
else:
246-
print("Usage: python3 ___.py <-e||-s> <path_to_grid_file>")
247-
return -1
248-
return grid
249-
250-
251-
def parse_args_empty(argv: list[str]) -> list[list[Node]]:
252-
"""
253-
Parse the system arguments and returns the empty grid.
254-
"""
255-
import os
256-
if len(argv) != 2:
257-
print(f"Usage: python3 ___.py <path>")
258-
return -1
259-
path = argv[1]
260-
if not os.path.isfile(path):
261-
print("ERROR: File does not exist")
262-
return -1
263-
grid = import_empty_grid(path)
264-
return grid
265-
266-
267-
def parse_to_path(argv: list[str]) -> str:
268-
"""
269-
Parse the system arguments and returns the path.
270-
"""
271-
import os
272-
if len(argv) != 2:
273-
print(f"Usage: python3 ___.py <path>")
274-
return -1
275-
path = argv[1]
276-
if os.path.isfile(path):
277-
print(f"ERROR: There is an existing file at '{path}'")
278-
return -1
279-
return path
280-
281-
282230
def main():
283231
import sys
284232
grid = parse_args(sys.argv)

hashi/generator.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,8 @@
5151
from node import Node, direction_to_vector, is_in_grid
5252
from random import randint, choice
5353
from visualiser import draw_grid
54-
from export import save_grid, parse_to_path
54+
from export import save_grid
55+
from arg_parser import parse_to_path
5556

5657

5758
def get_random_direction(grid: list[list[Node]], x: int, y: int) -> int:

hashi/node.py

+6
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,12 @@ def __init__(self, x, y):
2121
def needed(self) -> int:
2222
return self.i_count - self.current_in
2323

24+
def make_empty(self) -> None:
25+
self.n_type = 0
26+
self.i_count = -1
27+
self.b_thickness = -1
28+
self.b_dir = -1
29+
2430
def make_island(self, i_count: int) -> None:
2531
self.n_type = 1
2632
self.i_count = i_count

hashi/production.py

+88
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
"""
2+
3+
"""
4+
5+
from node import Node
6+
from generator import generate_till_full
7+
from export import save_grid
8+
from solver import solve
9+
from cathegorise import determine_difficulty
10+
11+
# DIRECTORIES
12+
import os
13+
""" --STRUCTURE--
14+
project/
15+
hashi/
16+
production.py
17+
...
18+
database/
19+
easy/
20+
intermediate/
21+
hard/
22+
"""
23+
DATABASE_DIR: str = os.path.abspath(os.path.join(os.path.dirname(__file__), '../database'))
24+
EASY_DIR: str = os.path.join(DATABASE_DIR, "easy")
25+
INTERMEDIATE_DIR: str = os.path.join(DATABASE_DIR, "intermediate")
26+
HARD_DIR: str = os.path.join(DATABASE_DIR, "hard")
27+
assert os.path.isdir(DATABASE_DIR)
28+
assert os.path.isdir(EASY_DIR)
29+
assert os.path.isdir(INTERMEDIATE_DIR)
30+
assert os.path.isdir(HARD_DIR)
31+
easy_puzzle_count: int = len(os.listdir(EASY_DIR))
32+
intermediate_puzzle_count: int = len(os.listdir(INTERMEDIATE_DIR))
33+
hard_puzzle_count: int = len(os.listdir(HARD_DIR))
34+
35+
36+
def is_completed(grid: list[list[Node]]) -> bool:
37+
"""
38+
Checks if the grid is completed.
39+
"""
40+
for row in grid:
41+
for node in row:
42+
if node.needed != 0:
43+
return False
44+
return True
45+
46+
47+
def save_according_to_difficulty(grid: list[list[Node]], difficulty: int) -> None:
48+
"""
49+
Saves the grid according to its difficulty.
50+
"""
51+
global easy_puzzle_count, intermediate_puzzle_count, hard_puzzle_count
52+
puzzle_path = None
53+
if difficulty < 0.3:
54+
puzzle_path = os.path.join(EASY_DIR, f"puzzle_{easy_puzzle_count}.csv")
55+
easy_puzzle_count += 1
56+
elif difficulty < 0.6:
57+
puzzle_path = os.path.join(INTERMEDIATE_DIR, f"puzzle_{intermediate_puzzle_count}.csv")
58+
intermediate_puzzle_count += 1
59+
else:
60+
puzzle_path = os.path.join(HARD_DIR, f"puzzle_{hard_puzzle_count}.csv")
61+
hard_puzzle_count += 1
62+
assert puzzle_path is not None
63+
save_grid(grid, puzzle_path)
64+
65+
66+
def produce(amount: int) -> None:
67+
"""
68+
Steps:
69+
1. Generate a full grid
70+
2. Solve it and determine the difficulty
71+
3. Save it according to the difficulty
72+
"""
73+
74+
while amount > 0:
75+
grid = generate_till_full()
76+
solved_grid, step_count = solve(grid)
77+
if not is_completed(solved_grid): return # if the grid is not solvable, don't save it
78+
difficulty = determine_difficulty(solved_grid, step_count)
79+
save_according_to_difficulty(solved_grid, difficulty)
80+
amount -= 1
81+
82+
83+
def main() -> None:
84+
pass
85+
86+
87+
if __name__ == "__main__":
88+
main()

0 commit comments

Comments
 (0)