Skip to content

Commit 6c08d21

Browse files
committed
Added day 2017-21 and a drawing library (not very efficient...)
1 parent 60f7527 commit 6c08d21

File tree

2 files changed

+256
-0
lines changed

2 files changed

+256
-0
lines changed

2017/21-Fractal Art.py

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
# -------------------------------- Input data -------------------------------- #
2+
import os, drawing, itertools, math
3+
4+
test_data = {}
5+
6+
test = 1
7+
test_data[test] = {"input": """../.# => ##./#../...
8+
.#./..#/### => #..#/..../..../#..#""",
9+
"expected": ['12', 'Unknown'],
10+
}
11+
12+
test = 'real'
13+
input_file = os.path.join(os.path.dirname(__file__), 'Inputs', os.path.basename(__file__).replace('.py', '.txt'))
14+
test_data[test] = {"input": open(input_file, "r+").read().strip(),
15+
"expected": ['139', '1857134'],
16+
}
17+
18+
# -------------------------------- Control program execution -------------------------------- #
19+
20+
case_to_test = 'real'
21+
part_to_test = 2
22+
verbose_level = 1
23+
24+
# -------------------------------- Initialize some variables -------------------------------- #
25+
26+
puzzle_input = test_data[case_to_test]['input']
27+
puzzle_expected_result = test_data[case_to_test]['expected'][part_to_test-1]
28+
puzzle_actual_result = 'Unknown'
29+
30+
31+
# -------------------------------- Actual code execution -------------------------------- #
32+
33+
pattern = '''.#.
34+
..#
35+
###'''
36+
37+
grid = drawing.text_to_grid(pattern)
38+
parts = drawing.split_in_parts(grid, 2, 2)
39+
merged_grid = drawing.merge_parts(parts, 2, 2)
40+
41+
42+
if case_to_test == 1:
43+
iterations = 2
44+
elif part_to_test == 1:
45+
iterations = 5
46+
else:
47+
iterations = 18
48+
49+
50+
enhancements = {}
51+
for string in puzzle_input.split('\n'):
52+
if string == '':
53+
continue
54+
55+
source, _, target = string.split(' ')
56+
source = source.replace('/', '\n')
57+
target = target.replace('/', '\n')
58+
59+
source_grid = drawing.text_to_grid(source)
60+
enhancements[source] = target
61+
62+
for rotated_source in drawing.rotate(source_grid):
63+
rotated_source_text = drawing.grid_to_text(rotated_source)
64+
enhancements[rotated_source_text] = target
65+
66+
for flipped_source in drawing.flip(rotated_source):
67+
flipped_source_text = drawing.grid_to_text(flipped_source)
68+
enhancements[flipped_source_text] = target
69+
70+
pattern_grid = drawing.text_to_grid(pattern)
71+
for i in range(iterations):
72+
73+
grid_x, grid_y = zip(*pattern_grid.keys())
74+
grid_width = max(grid_x) - min(grid_x) + 1
75+
76+
if grid_width % 2 == 0:
77+
parts = drawing.split_in_parts(pattern_grid, 2, 2)
78+
else:
79+
parts = drawing.split_in_parts(pattern_grid, 3, 3)
80+
81+
grid_size = int(math.sqrt(len(parts)))
82+
83+
new_parts = []
84+
for part in parts:
85+
part_text = drawing.grid_to_text(part)
86+
new_parts.append(drawing.text_to_grid(enhancements[part_text]))
87+
88+
new_grid = drawing.merge_parts(new_parts, grid_size, grid_size)
89+
90+
pattern_grid = new_grid
91+
92+
grid_text = drawing.grid_to_text(pattern_grid)
93+
94+
puzzle_actual_result = grid_text.count('#')
95+
96+
97+
98+
# -------------------------------- Outputs / results -------------------------------- #
99+
100+
if verbose_level >= 3:
101+
print ('Input : ' + puzzle_input)
102+
print ('Expected result : ' + str(puzzle_expected_result))
103+
print ('Actual result : ' + str(puzzle_actual_result))
104+
105+
106+
107+

2017/drawing.py

Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
import math, os
2+
3+
4+
def text_to_grid (text):
5+
"""
6+
Converts a text to a set of coordinates
7+
8+
The text is expected to be separated by newline characters
9+
Each character will have its coordinates as keys
10+
11+
:param string text: The text to convert
12+
:return: The converted grid, its height and width
13+
"""
14+
grid = {}
15+
lines = text.splitlines()
16+
height = len(lines)
17+
width = 0
18+
for y in range(len(lines)):
19+
width = max(width, len(lines[y]))
20+
for x in range(len(lines[y])):
21+
grid[(x, y)] = lines[y][x]
22+
23+
return grid
24+
25+
def grid_to_text (grid, blank_character = ' '):
26+
"""
27+
Converts the grid to a text format
28+
29+
:param dict grid: The grid to convert, in format (x, y): value
30+
:param string blank_character: What to use for cells with unknown value
31+
:return: The grid in text format
32+
"""
33+
34+
text = ''
35+
36+
grid_x, grid_y = zip(*grid.keys())
37+
38+
for y in range (min(grid_y), max(grid_y)+1):
39+
for x in range (min(grid_x), max(grid_x)+1):
40+
if (x, y) in grid:
41+
text += grid[(x, y)]
42+
else:
43+
text += blank_character
44+
text += os.linesep
45+
text = text[:-len(os.linesep)]
46+
47+
return text
48+
49+
def split_in_parts (grid, width, height):
50+
"""
51+
Splits a grid in parts of width*height size
52+
53+
:param dict grid: The grid to convert, in format (x, y): value
54+
:param integer width: The width of parts to use
55+
:param integer height: The height of parts to use
56+
:return: The different parts
57+
"""
58+
59+
if not isinstance(width, int) or not isinstance(height, int):
60+
return False
61+
if width <= 0 or height <= 0:
62+
return False
63+
64+
grid_x, grid_y = zip(*grid.keys())
65+
grid_width = max(grid_x) - min(grid_x) + 1
66+
grid_height = max(grid_y) - min(grid_y) + 1
67+
68+
parts = []
69+
70+
for part_y in range(math.ceil(grid_height / height)):
71+
for part_x in range (math.ceil(grid_width / width)):
72+
parts.append({(x, y):grid[(x, y)] \
73+
for x in range(part_x*width, min((part_x + 1)*width, grid_width)) \
74+
for y in range(part_y*height, min((part_y + 1)*height, grid_height))})
75+
76+
return parts
77+
78+
def merge_parts (parts, width, height):
79+
"""
80+
Merges different parts in a single grid
81+
82+
:param dict parts: The parts to merge, in format (x, y): value
83+
:return: The merged grid
84+
"""
85+
86+
grid = {}
87+
88+
part_x, part_y = zip(*parts[0].keys())
89+
part_width = max(part_x) - min(part_x) + 1
90+
part_height = max(part_y) - min(part_y) + 1
91+
92+
part_nr = 0
93+
for part_y in range(height):
94+
for part_x in range(width):
95+
grid.update({(x + part_x*part_width, y + part_y*part_height): parts[part_nr][(x, y)] for (x, y) in parts[part_nr]})
96+
part_nr += 1
97+
98+
return grid
99+
100+
def rotate (grid, rotations = (0, 90, 180, 270)):
101+
"""
102+
Rotates a grid and returns the result
103+
104+
:param dict grid: The grid to rotate, in format (x, y): value
105+
:param tuple rotations: Which angles to use for rotation
106+
:return: The parts in text format
107+
"""
108+
109+
rotated_grid = []
110+
111+
grid_x, grid_y = zip(*grid.keys())
112+
width = max(grid_x) - min(grid_x) + 1
113+
height = max(grid_y) - min(grid_y) + 1
114+
115+
for angle in rotations:
116+
if angle == 0:
117+
rotated_grid.append(grid)
118+
elif angle == 90:
119+
rotated_grid.append({(height-y, x): grid[(x, y)] for (x, y) in grid})
120+
elif angle == 180:
121+
rotated_grid.append({(width-x, height-y): grid[(x, y)] for (x, y) in grid})
122+
elif angle == 270:
123+
rotated_grid.append({(y, width-x): grid[(x, y)] for (x, y) in grid})
124+
125+
return rotated_grid
126+
127+
def flip (grid, flips = ('V', 'H')):
128+
"""
129+
Flips a grid and returns the result
130+
131+
:param dict grid: The grid to rotate, in format (x, y): value
132+
:param tuple flips: Which flips (horizontal, vertical) to use for flip
133+
:return: The parts in text format
134+
"""
135+
136+
flipped_grid = []
137+
138+
grid_x, grid_y = zip(*grid.keys())
139+
width = max(grid_x) - min(grid_x) + 1
140+
height = max(grid_y) - min(grid_y) + 1
141+
142+
for flip in flips:
143+
if flip == 'H':
144+
flipped_grid.append({(x, height-y): grid[(x, y)] for (x, y) in grid})
145+
elif flip == 'V':
146+
flipped_grid.append({(width-x, y): grid[(x, y)] for (x, y) in grid})
147+
148+
return flipped_grid
149+

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy