gen-80x25-crossword - crossword-generator - A crossword vtv generator. | |
git clone git://bitreich.org/crossword-generator git://enlrupgkhuxnvlhsf6lc3fzi… | |
Log | |
Files | |
Refs | |
Tags | |
LICENSE | |
--- | |
gen-80x25-crossword (5644B) | |
--- | |
1 #!/usr/bin/env python | |
2 # coding=utf-8 | |
3 # | |
4 # © 2023 Christoph Lohmann <[email protected]> | |
5 # | |
6 # This file is published under the terms of the GPLv3. | |
7 # | |
8 | |
9 import os | |
10 import sys | |
11 import getopt | |
12 import random | |
13 | |
14 def print_field(field): | |
15 for line in field: | |
16 for cell in line: | |
17 print("%s" % (cell), end="") | |
18 print("") | |
19 | |
20 def usage(app): | |
21 app = os.path.basename(app) | |
22 print("usage: %s [-h] file.txt" % (app), file=sys.stderr) | |
23 sys.exit(1) | |
24 | |
25 def main(args): | |
26 try: | |
27 opts, largs = getopt.getopt(args[1:], "h") | |
28 except getopt.GetoptError as err: | |
29 print(str(err)) | |
30 usage(args[0]) | |
31 | |
32 for o, a in opts: | |
33 if o == "-h": | |
34 usage(args[0]) | |
35 else: | |
36 assert False, "unhandled option" | |
37 | |
38 if len(largs) < 1: | |
39 usage(args[0]) | |
40 crossfile = largs[0] | |
41 entries = {} | |
42 | |
43 with open(crossfile) as fd: | |
44 for line in fd: | |
45 line = line.strip() | |
46 if len(line) == 0: | |
47 continue | |
48 if line[0] == "#": | |
49 continue | |
50 try: | |
51 (word, hint) = line.split(" ", 1) | |
52 except ValueError: | |
53 continue | |
54 entries[word] = hint | |
55 if entries == {}: | |
56 usage(args[0]) | |
57 #print(entries) | |
58 | |
59 def check_word(field, x, y, word, vertical): | |
60 #print("check_word(field, %s, %s, %s, %s)" % \ | |
61 # (x, y, word, vertical)) | |
62 if y < 1 or x < 1: | |
63 return False | |
64 for c in word: | |
65 #print("check '%s' vs '%s' at (%s,%s)" % \ | |
66 # (field[y][x], c, x, y)) | |
67 if y > 24: | |
68 return False | |
69 if x > 79: | |
70 return False | |
71 if field[y][x] == '#': | |
72 return False | |
73 if field[y][x] != c and field[y][x] != '.': | |
74 return False | |
75 if vertical: | |
76 y += 1 | |
77 else: | |
78 x += 1 | |
79 return True | |
80 | |
81 def add_word(field, x, y, word, vertical): | |
82 #print("add_word %s %s %s %s %s" % (x, y, word, len(word… | |
83 # vertical)) | |
84 for c in word: | |
85 try: | |
86 field[y][x] = c | |
87 except IndexError: | |
88 print("x = %s, y = %s" % (x, y)) | |
89 sys.exit(1) | |
90 if c not in char2pos: | |
91 char2pos[c] = [] | |
92 char2pos[c] += [[x, y, vertical]] | |
93 if vertical: | |
94 y += 1 | |
95 else: | |
96 x += 1 | |
97 | |
98 def replace_word(field, x, y, word, vertical, c): | |
99 #print("replace_word %s %s %s %s %s %s" % (x, y, word, l… | |
100 # vertical, c)) | |
101 for b in word: | |
102 try: | |
103 if c == "=": | |
104 field[y][x] = c | |
105 elif field[y][x] != "=": | |
106 field[y][x] = "X" | |
107 else: | |
108 field[y][x] = c | |
109 except IndexError: | |
110 print("x = %s, y = %s" % (x, y)) | |
111 sys.exit(1) | |
112 if vertical: | |
113 y += 1 | |
114 else: | |
115 x += 1 | |
116 | |
117 def print_description(field, x, y, description): | |
118 for c in description: | |
119 try: | |
120 field[y][x] = c | |
121 except IndexError: | |
122 print("x = %s, y = %s" % (x, y)) | |
123 sys.exit(1) | |
124 x += 1 | |
125 if x > 78: | |
126 break | |
127 | |
128 maxtries = 10 | |
129 tries = 0 | |
130 entriesbackup = entries.copy() | |
131 while tries < maxtries: | |
132 # 80x25 for term output. | |
133 field = [] | |
134 field.append(["#"]*80) | |
135 for i in range(1, 24): | |
136 field.append(["#"] + ["."]*78 + ["#"]) | |
137 field.append(["#"]*80) | |
138 char2pos = {} | |
139 | |
140 field.append(["#"]*80) | |
141 for i in range(1, 24): | |
142 field.append(["#"] + ["."]*78 + ["#"]) | |
143 field.append(["#"]*80) | |
144 | |
145 entries = entriesbackup.copy() | |
146 print_description(field, 45, 3, "A funny crossword from … | |
147 print_description(field, 47, 5, "Have fun solving it!") | |
148 print_description(field, 42, 7, "[a-z0-9] -> find the ma… | |
149 print_description(field, 49, 8, "X -> crossover of names… | |
150 print_description(field, 30, 10, "= manpage description … | |
151 outputentries = {} | |
152 markerstr = "abcdefghijklmnopqrstuvwxyz0123456789" | |
153 ypos = 11 | |
154 xpos = 30 | |
155 markeri = 0 | |
156 for entry in entries.keys(): | |
157 entries[entry] = [entries[entry], markerstr[mark… | |
158 if ypos == 24: | |
159 xpos = 2 | |
160 ypos += 2 | |
161 if ypos < len(field) - 1: | |
162 print_description(field, xpos, ypos, \ | |
163 "%s %s" % (entries[entry][1], \ | |
164 entries[entry][0])) | |
165 ypos += 1 | |
166 markeri += 1 | |
167 | |
168 entry = random.choice(list(entries.keys())) | |
169 vertical = random.choice([True, False]) | |
170 x = 5 | |
171 y = 12 | |
172 #x = random.randint(0 + 5, 5 + 20 - len(entry) * int(not… | |
173 #y = random.randint(0 + 5, 5 + 10 - len(entry) * int(ver… | |
174 add_word(field, x, y, entry, vertical) | |
175 del entries[entry] | |
176 | |
177 tryouts = [] | |
178 while len(entries.keys()) > 0: | |
179 ex = 0 | |
180 ey = 0 | |
181 vertical = True | |
182 entryfound = False | |
183 entry = random.choice(list(entries.keys())) | |
184 #print("Trying %s ..." % (entry), end="") | |
185 if entry in tryouts: | |
186 #print("") | |
187 break | |
188 | |
189 sentry = "".join(random.sample(entry, len(entry)… | |
190 for c in sentry: | |
191 if c not in char2pos: | |
192 continue | |
193 (beg, remain) = entry.split(c, 1) | |
194 randpos = random.sample(char2pos[c], len… | |
195 for pos in randpos: | |
196 vertical = not pos[2] | |
197 ex = pos[0] - len(beg) * int(not… | |
198 ey = pos[1] - len(beg) * int(ver… | |
199 if check_word(field, ex, ey, ent… | |
200 #print(" entryfound", en… | |
201 entryfound = True | |
202 break | |
203 if entryfound == True: | |
204 break | |
205 #print("") | |
206 if entryfound == True: | |
207 add_word(field, ex, ey, entry, vertical) | |
208 outputentries[entry] = \ | |
209 [entries[entry][0], \ | |
210 entries[entry][1], \ | |
211 ex, ey, vertical] | |
212 del entries[entry] | |
213 else: | |
214 tryouts.append(entry) | |
215 if len(entries) == 0: | |
216 for entry in outputentries.keys(): | |
217 replace_word(field, \ | |
218 outputentries[entry][2], \ | |
219 outputentries[entry][3], \ | |
220 entry, \ | |
221 outputentries[entry][4], \ | |
222 "=") | |
223 | |
224 for entry in outputentries.keys(): | |
225 replace_word(field, \ | |
226 outputentries[entry][2], \ | |
227 outputentries[entry][3], \ | |
228 entry, \ | |
229 outputentries[entry][4], \ | |
230 outputentries[entry][1]) | |
231 break | |
232 tries += 1 | |
233 | |
234 if tries >= maxtries: | |
235 sys.stderr.write("Crossword not possible.\n") | |
236 #print(char2pos) | |
237 print_field(field) | |
238 return 1 | |
239 | |
240 print_field(field) | |
241 return 0 | |
242 | |
243 if __name__ == "__main__": | |
244 sys.exit(main(sys.argv)) | |
245 |