schrum2 commited on
Commit
bd0b9fc
·
verified ·
1 Parent(s): b9c2e7e

Delete util.py

Browse files
Files changed (1) hide show
  1. util.py +0 -216
util.py DELETED
@@ -1,216 +0,0 @@
1
- import json
2
- import sys
3
- import os
4
- from collections import Counter
5
-
6
- # This file contains utility functions for analyzing and describing levels in both Lode Runner and Super Mario Bros.
7
-
8
- # Could define these via the command line, but for now they are hardcoded
9
- coarse_locations = True
10
- coarse_counts = True
11
- pluralize = True
12
- give_staircase_lengths = False
13
-
14
- def describe_size(count):
15
- if count <= 4: return "small"
16
- else: return "big"
17
-
18
- def describe_quantity(count):
19
- if count == 0: return "no"
20
- elif count == 1: return "one"
21
- elif count == 2: return "two"
22
- elif count < 5: return "a few"
23
- elif count < 10: return "several"
24
- else: return "many"
25
-
26
- def get_tile_descriptors(tileset):
27
- """Creates a mapping from tile character to its list of descriptors."""
28
- result = {char: set(attrs) for char, attrs in tileset["tiles"].items()}
29
- # Fake tiles. Should these contain anything? Note that code elsewhere expects everything to be passable or solid
30
- result["!"] = {"passable"}
31
- result["*"] = {"passable"}
32
- return result
33
-
34
- def analyze_floor(scene, id_to_char, tile_descriptors, describe_absence):
35
- """Analyzes the last row of the 32x32 scene and generates a floor description."""
36
- WIDTH = len(scene[0])
37
- last_row = scene[-1] # The FLOOR row of the scene
38
- solid_count = sum(
39
- 1 for tile in last_row
40
- if tile in id_to_char and (
41
- "solid" in tile_descriptors.get(id_to_char[tile], []) or
42
- "diggable" in tile_descriptors.get(id_to_char[tile], [])
43
- )
44
- )
45
- passable_count = sum(
46
- 1 for tile in last_row if "passable" in tile_descriptors.get(id_to_char[tile], [])
47
- )
48
-
49
- if solid_count == WIDTH:
50
- return "full floor"
51
- elif passable_count == WIDTH:
52
- if describe_absence:
53
- return "no floor"
54
- else:
55
- return ""
56
- elif solid_count > passable_count:
57
- # Count contiguous groups of passable tiles
58
- gaps = 0
59
- in_gap = False
60
- for tile in last_row:
61
- # Enemies are also a gap since they immediately fall into the gap
62
- if "passable" in tile_descriptors.get(id_to_char[tile], []) or "enemy" in tile_descriptors.get(id_to_char[tile], []):
63
- if not in_gap:
64
- gaps += 1
65
- in_gap = True
66
- elif "solid" in tile_descriptors.get(id_to_char[tile], []):
67
- in_gap = False
68
- else:
69
- print("error")
70
- print(tile)
71
- print(id_to_char[tile])
72
- print(tile_descriptors)
73
- print(tile_descriptors.get(id_to_char[tile], []))
74
- raise ValueError("Every tile should be passable, solid, or enemy")
75
- return f"floor with {describe_quantity(gaps) if coarse_counts else gaps} gap" + ("s" if pluralize and gaps != 1 else "")
76
- else:
77
- # Count contiguous groups of solid tiles
78
- chunks = 0
79
- in_chunk = False
80
- for tile in last_row:
81
- if "solid" in tile_descriptors.get(id_to_char[tile], []):
82
- if not in_chunk:
83
- chunks += 1
84
- in_chunk = True
85
- elif "passable" in tile_descriptors.get(id_to_char[tile], []) or "enemy" in tile_descriptors.get(id_to_char[tile], []):
86
- in_chunk = False
87
- else:
88
- print("error")
89
- print(tile)
90
- print(tile_descriptors)
91
- print(tile_descriptors.get(tile, []))
92
- raise ValueError("Every tile should be either passable or solid")
93
- return f"giant gap with {describe_quantity(chunks) if coarse_counts else chunks} chunk"+("s" if pluralize and chunks != 1 else "")+" of floor"
94
-
95
- def count_in_scene(scene, tiles, exclude=set()):
96
- """ counts standalone tiles, unless they are in the exclude set """
97
- count = 0
98
- for r, row in enumerate(scene):
99
- for c, t in enumerate(row):
100
- #if exclude and t in tiles: print(r,c, exclude)
101
- if (r,c) not in exclude and t in tiles:
102
- #if exclude: print((r,t), exclude, (r,t) in exclude)
103
- count += 1
104
- #if exclude: print(tiles, exclude, count)
105
- return count
106
-
107
- def count_caption_phrase(scene, tiles, name, names, offset = 0, describe_absence=False, exclude=set()):
108
- """ offset modifies count used in caption """
109
- count = offset + count_in_scene(scene, tiles, exclude)
110
- #if name == "loose block": print("count", count)
111
- if count > 0:
112
- return f" {describe_quantity(count) if coarse_counts else count} " + (names if pluralize and count > 1 else name) + "."
113
- elif describe_absence:
114
- return f" no {names}."
115
- else:
116
- return ""
117
-
118
- def in_column(scene, x, tile):
119
- for row in scene:
120
- if row[x] == tile:
121
- return True
122
-
123
- return False
124
-
125
- def analyze_ceiling(scene, id_to_char, tile_descriptors, describe_absence, ceiling_row = 1):
126
- """
127
- Analyzes ceiling row (0-based index) to detect a ceiling.
128
- Returns a caption phrase or an empty string if no ceiling is detected.
129
- """
130
- WIDTH = len(scene[0])
131
-
132
- row = scene[ceiling_row]
133
- solid_count = sum(1 for tile in row if "solid" in tile_descriptors.get(id_to_char[tile], []))
134
-
135
- if solid_count == WIDTH:
136
- return " full ceiling."
137
- elif solid_count > WIDTH//2:
138
- # Count contiguous gaps of passable tiles
139
- gaps = 0
140
- in_gap = False
141
- for tile in row:
142
- # Enemies are also a gap since they immediately fall into the gap, but they are marked as "moving" and not "passable"
143
- if "passable" in tile_descriptors.get(id_to_char[tile], []) or "moving" in tile_descriptors.get(id_to_char[tile], []):
144
- if not in_gap:
145
- gaps += 1
146
- in_gap = True
147
- else:
148
- in_gap = False
149
- result = f" ceiling with {describe_quantity(gaps) if coarse_counts else gaps} gap" + ("s" if pluralize and gaps != 1 else "") + "."
150
-
151
- # Adding the "moving" check should make this code unnecessary
152
- #if result == ' ceiling with no gaps.':
153
- # print("This should not happen: ceiling with no gaps")
154
- # print("ceiling_row:", scene[ceiling_row])
155
- # result = " full ceiling."
156
-
157
- return result
158
- elif describe_absence:
159
- return " no ceiling."
160
- else:
161
- return "" # Not enough solid tiles for a ceiling
162
-
163
- def extract_tileset(tileset_path):
164
- # Load tileset
165
- with open(tileset_path, "r") as f:
166
- tileset = json.load(f)
167
- #print(f"tileset: {tileset}")
168
- tile_chars = sorted(tileset['tiles'].keys())
169
- # Wiggle room for the tileset to be a bit more flexible.
170
- # However, this requires me to add some bogus tiles to the list.
171
- # tile_chars.append('!')
172
- # tile_chars.append('*')
173
- #print(f"tile_chars: {tile_chars}")
174
- id_to_char = {idx: char for idx, char in enumerate(tile_chars)}
175
- #print(f"id_to_char: {id_to_char}")
176
- char_to_id = {char: idx for idx, char in enumerate(tile_chars)}
177
- #print(f"char_to_id: {char_to_id}")
178
- tile_descriptors = get_tile_descriptors(tileset)
179
- #print(f"tile_descriptors: {tile_descriptors}")
180
-
181
- return tile_chars, id_to_char, char_to_id, tile_descriptors
182
-
183
- def flood_fill(scene, visited, start_row, start_col, id_to_char, tile_descriptors, excluded, pipes=False, target_descriptor=None):
184
- stack = [(start_row, start_col)]
185
- structure = []
186
-
187
- while stack:
188
- row, col = stack.pop()
189
- if (row, col) in visited or (row, col) in excluded:
190
- continue
191
- tile = scene[row][col]
192
- descriptors = tile_descriptors.get(id_to_char[tile], [])
193
- # Use target_descriptor if provided, otherwise default to old solid/pipe logic
194
- if target_descriptor is not None:
195
- if target_descriptor not in descriptors:
196
- continue
197
- else:
198
- if "solid" not in descriptors or (not pipes and "pipe" in descriptors) or (pipes and "pipe" not in descriptors):
199
- continue
200
-
201
- visited.add((row, col))
202
- structure.append((row, col))
203
-
204
- # Check neighbors
205
- for d_row, d_col in [(-1,0), (1,0), (0,-1), (0,1)]:
206
- # Weird special case for adjacent pipes
207
- if (id_to_char[tile] == '>' or id_to_char[tile] == ']') and d_col == 1: # if on the right edge of a pipe
208
- continue # Don't go right if on the right edge of a pipe
209
- if (id_to_char[tile] == '<' or id_to_char[tile] == '[') and d_col == -1: # if on the left edge of a pipe
210
- continue # Don't go left if on the left edge of a pipe
211
-
212
- n_row, n_col = row + d_row, col + d_col
213
- if 0 <= n_row < len(scene) and 0 <= n_col < len(scene[0]):
214
- stack.append((n_row, n_col))
215
-
216
- return structure