utarn commited on
Commit
f48fe4d
·
1 Parent(s): 4c9b2e2

Initial commit of maze generator app

Browse files
Files changed (6) hide show
  1. .gitignore +12 -0
  2. README.md +42 -5
  3. app.py +187 -0
  4. main.py +320 -0
  5. maze_generator.py +136 -0
  6. requirements.txt +4 -0
.gitignore ADDED
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Python-generated files
2
+ __pycache__/
3
+ *.py[oc]
4
+ build/
5
+ dist/
6
+ wheels/
7
+ *.egg-info
8
+
9
+ # Virtual environments
10
+ .venv
11
+ pdfs
12
+ .cache
README.md CHANGED
@@ -1,13 +1,50 @@
1
  ---
2
  title: Maze Generator
3
- emoji: 📈
4
  colorFrom: blue
5
- colorTo: green
6
  sdk: gradio
7
- sdk_version: 5.49.1
8
  app_file: app.py
9
  pinned: false
10
- short_description: The maze generator for kids
11
  ---
12
 
13
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  ---
2
  title: Maze Generator
3
+ emoji: 🎯
4
  colorFrom: blue
5
+ colorTo: purple
6
  sdk: gradio
7
+ sdk_version: 4.0.0
8
  app_file: app.py
9
  pinned: false
10
+ license: mit
11
  ---
12
 
13
+ # Maze Generator
14
+
15
+ A Gradio web interface that generates custom maze PDFs from https://www.mazegenerator.net/ with customizable dimensions and quantities.
16
+
17
+ ## Features
18
+
19
+ - Generate multiple maze PDFs with specified dimensions
20
+ - Extract only the first page from each PDF (to remove solution pages)
21
+ - Web interface with progress tracking
22
+ - Merge multiple PDFs into a single file
23
+ - Real-time file list updates
24
+
25
+ ## How to Use
26
+
27
+ 1. **Generate Mazes Tab:**
28
+ - Set the number of mazes you want to generate (1-50)
29
+ - Specify width and height in cells (2-200)
30
+ - Click "Generate Mazes" to create the PDFs
31
+ - View generated files in the file list
32
+
33
+ 2. **Merge PDFs Tab:**
34
+ - Click "Merge All PDFs" to combine all PDFs in the pdfs/ directory
35
+ - The merged file will be saved as `merged_mazes.pdf`
36
+ - Individual files will be deleted after merging
37
+
38
+ ## Technical Details
39
+
40
+ - Each maze is generated with random patterns
41
+ - PDFs are saved in the `pdfs/` directory
42
+ - File naming pattern: `maze_{width}x{height}_{number}.pdf`
43
+ - The app automatically handles file name conflicts by incrementing numbers
44
+
45
+ ## Dependencies
46
+
47
+ - `gradio`: Web interface framework
48
+ - `pandas`: Data manipulation
49
+ - `pypdf`: PDF processing and merging
50
+ - `requests`: HTTP requests to maze generator API
app.py ADDED
@@ -0,0 +1,187 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ import gradio as gr
3
+ import os
4
+ import threading
5
+ from main import generate_multiple_mazes, merge_pdfs
6
+
7
+ def generate_mazes_interface(num_files, width, height, progress=gr.Progress()):
8
+ """Interface function for generating mazes with progress tracking"""
9
+ try:
10
+ progress(0.1, desc="Starting maze generation...")
11
+
12
+ # Validate parameters
13
+ if num_files < 1:
14
+ return "Error: Number of files must be at least 1", None
15
+
16
+ if not (2 <= width <= 200):
17
+ return "Error: Width must be between 2 and 200", None
18
+
19
+ if not (2 <= height <= 200):
20
+ return "Error: Height must be between 2 and 200", None
21
+
22
+ progress(0.2, desc="Generating mazes...")
23
+
24
+ # Generate the mazes
25
+ success_count = generate_multiple_mazes(num_files, width, height)
26
+
27
+ progress(1.0, desc="Generation complete!")
28
+
29
+ if success_count == num_files:
30
+ return f"Successfully generated {success_count} maze PDFs in pdfs/ directory", get_pdf_list()
31
+ else:
32
+ return f"Warning: Only {success_count} out of {num_files} mazes were generated successfully", get_pdf_list()
33
+
34
+ except Exception as e:
35
+ return f"Error: {str(e)}", None
36
+
37
+ def merge_pdfs_interface(progress=gr.Progress()):
38
+ """Interface function for merging PDFs with progress tracking"""
39
+ try:
40
+ progress(0.1, desc="Starting PDF merge...")
41
+
42
+ if not os.path.exists('pdfs'):
43
+ return "Error: pdfs/ directory does not exist", None
44
+
45
+ progress(0.5, desc="Merging PDFs...")
46
+
47
+ success = merge_pdfs('pdfs')
48
+
49
+ progress(1.0, desc="Merge complete!")
50
+
51
+ if success:
52
+ return "Successfully merged all PDFs into pdfs/merged_mazes.pdf", get_pdf_list()
53
+ else:
54
+ return "Failed to merge PDFs", get_pdf_list()
55
+
56
+ except Exception as e:
57
+ return f"Error: {str(e)}", None
58
+
59
+ def get_pdf_list():
60
+ """Get list of PDF files in pdfs directory"""
61
+ try:
62
+ if os.path.exists('pdfs'):
63
+ pdf_files = [f for f in os.listdir('pdfs') if f.endswith('.pdf')]
64
+ pdf_files.sort()
65
+ return pdf_files if pdf_files else ["No PDF files found"]
66
+ else:
67
+ return ["pdfs/ directory does not exist"]
68
+ except Exception as e:
69
+ return [f"Error: {str(e)}"]
70
+
71
+ def refresh_file_list():
72
+ """Refresh the file list"""
73
+ return get_pdf_list()
74
+
75
+ def create_interface():
76
+ """Create the Gradio interface"""
77
+ with gr.Blocks(title="Maze Generator", theme=gr.themes.Soft()) as interface:
78
+ gr.Markdown("# 🎯 Maze Generator")
79
+ gr.Markdown("Generate custom maze PDFs or merge existing ones using this web interface.")
80
+
81
+ with gr.Tabs():
82
+ with gr.TabItem("Generate Mazes"):
83
+ with gr.Row():
84
+ with gr.Column():
85
+ gr.Markdown("### Generate New Mazes")
86
+
87
+ num_files = gr.Number(
88
+ label="Number of Mazes",
89
+ value=1,
90
+ minimum=1,
91
+ maximum=50,
92
+ step=1
93
+ )
94
+
95
+ width = gr.Number(
96
+ label="Width (cells)",
97
+ value=25,
98
+ minimum=2,
99
+ maximum=200,
100
+ step=1
101
+ )
102
+
103
+ height = gr.Number(
104
+ label="Height (cells)",
105
+ value=35,
106
+ minimum=2,
107
+ maximum=200,
108
+ step=1
109
+ )
110
+
111
+ generate_btn = gr.Button("🚀 Generate Mazes", variant="primary", size="lg")
112
+
113
+ with gr.Column():
114
+ gr.Markdown("### Generated Files")
115
+ file_list = gr.List(
116
+ label="PDF Files in pdfs/ directory",
117
+ value=get_pdf_list()
118
+ )
119
+
120
+ refresh_btn = gr.Button("🔄 Refresh List", size="sm")
121
+
122
+ generate_output = gr.Textbox(
123
+ label="Status",
124
+ lines=3,
125
+ max_lines=5,
126
+ show_label=False,
127
+ container=True
128
+ )
129
+
130
+ # Event handlers
131
+ generate_btn.click(
132
+ fn=generate_mazes_interface,
133
+ inputs=[num_files, width, height],
134
+ outputs=[generate_output, file_list],
135
+ show_progress=True
136
+ )
137
+
138
+ refresh_btn.click(
139
+ fn=refresh_file_list,
140
+ outputs=[file_list]
141
+ )
142
+
143
+ with gr.TabItem("Merge PDFs"):
144
+ with gr.Row():
145
+ with gr.Column():
146
+ gr.Markdown("### Merge Existing PDFs")
147
+ gr.Markdown("Merge all PDF files in the pdfs/ directory into a single file and delete individual files.")
148
+
149
+ merge_btn = gr.Button("🔗 Merge All PDFs", variant="primary", size="lg")
150
+
151
+ merge_output = gr.Textbox(
152
+ label="Merge Status",
153
+ lines=3,
154
+ max_lines=5,
155
+ show_label=False,
156
+ container=True
157
+ )
158
+
159
+ merge_file_list = gr.List(
160
+ label="PDF Files After Merge",
161
+ value=get_pdf_list()
162
+ )
163
+
164
+ # Event handlers
165
+ merge_btn.click(
166
+ fn=merge_pdfs_interface,
167
+ outputs=[merge_output, merge_file_list],
168
+ show_progress=True
169
+ )
170
+
171
+ # Auto-refresh file list every few seconds
172
+ interface.load(
173
+ fn=lambda: (get_pdf_list(), get_pdf_list()),
174
+ outputs=[file_list, merge_file_list]
175
+ )
176
+
177
+ return interface
178
+
179
+ if __name__ == "__main__":
180
+ # Create and launch the interface
181
+ interface = create_interface()
182
+ interface.launch(
183
+ server_name="0.0.0.0",
184
+ server_port=7861,
185
+ share=False,
186
+ show_error=True
187
+ )
main.py ADDED
@@ -0,0 +1,320 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ import requests
3
+ import re
4
+ import argparse
5
+ import os
6
+ from urllib.parse import urljoin
7
+ from pypdf import PdfReader, PdfWriter
8
+
9
+ def get_maze_generator_page():
10
+ """Get the maze generator page to extract form data and session info"""
11
+ url = "https://www.mazegenerator.net/"
12
+ headers = {
13
+ 'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36'
14
+ }
15
+
16
+ response = requests.get(url, headers=headers)
17
+ return response.text, response.cookies
18
+
19
+ def extract_form_data(html_content):
20
+ """Extract form data and hidden fields from the page"""
21
+ # Look for form action URL
22
+ form_action_match = re.search(r'<form[^>]*action=["\']([^"\']*)["\']', html_content, re.IGNORECASE)
23
+ form_action = form_action_match.group(1) if form_action_match else ""
24
+
25
+ # Look for hidden inputs
26
+ hidden_inputs = re.findall(r'<input[^>]*type=["\']hidden["\'][^>]*name=["\']([^"\']*)["\'][^>]*value=["\']([^"\']*)["\']', html_content, re.IGNORECASE)
27
+ form_data = dict(hidden_inputs)
28
+
29
+ # Look for viewstate and other common ASP.NET fields
30
+ viewstate_match = re.search(r'name=["\']__VIEWSTATE["\'][^>]*value=["\']([^"\']*)["\']', html_content)
31
+ if viewstate_match:
32
+ form_data['__VIEWSTATE'] = viewstate_match.group(1)
33
+
34
+ viewstategen_match = re.search(r'name=["\']__VIEWSTATEGENERATOR["\'][^>]*value=["\']([^"\']*)["\']', html_content)
35
+ if viewstategen_match:
36
+ form_data['__VIEWSTATEGENERATOR'] = viewstategen_match.group(1)
37
+
38
+ eventvalidation_match = re.search(r'name=["\']__EVENTVALIDATION["\'][^>]*value=["\']([^"\']*)["\']', html_content)
39
+ if eventvalidation_match:
40
+ form_data['__EVENTVALIDATION'] = eventvalidation_match.group(1)
41
+
42
+ return form_action, form_data
43
+
44
+ def generate_maze(width=25, height=35):
45
+ """Generate a maze with specified dimensions"""
46
+ # Get the initial page
47
+ html_content, cookies = get_maze_generator_page()
48
+
49
+ # Extract form data
50
+ form_action, form_data = extract_form_data(html_content)
51
+
52
+ # Add maze parameters with correct field names from the HTML
53
+ form_data.update({
54
+ 'ShapeDropDownList': '1', # Rectangular
55
+ 'S1TesselationDropDownList': '1', # Orthogonal (Square cells)
56
+ 'S1WidthTextBox': str(width),
57
+ 'S1HeightTextBox': str(height),
58
+ 'S1InnerWidthTextBox': '0',
59
+ 'S1InnerHeightTextBox': '0',
60
+ 'S1StartsAtDropDownList': '1', # Top
61
+ 'AlgorithmParameter1TextBox': '50', # E parameter
62
+ 'AlgorithmParameter2TextBox': '100', # R parameter
63
+ 'GenerateButton': 'Generate'
64
+ })
65
+
66
+ # Make the request to generate the maze
67
+ headers = {
68
+ 'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36',
69
+ 'Content-Type': 'application/x-www-form-urlencoded',
70
+ 'Referer': 'https://www.mazegenerator.net/'
71
+ }
72
+
73
+ # Determine the full URL for the form submission
74
+ if form_action:
75
+ submit_url = urljoin('https://www.mazegenerator.net/', form_action)
76
+ else:
77
+ submit_url = 'https://www.mazegenerator.net/'
78
+
79
+ response = requests.post(submit_url, data=form_data, cookies=cookies, headers=headers)
80
+
81
+ return response.text, response.cookies
82
+
83
+ def download_pdf(html_content, cookies, filename="maze.pdf"):
84
+ """Download the maze as PDF by submitting the download form"""
85
+ # Extract form data for the download request
86
+ form_action, form_data = extract_form_data(html_content)
87
+
88
+ # Add download parameters
89
+ form_data.update({
90
+ 'FileFormatSelectorList': '1', # PDF (A4 size)
91
+ 'DownloadFileButton': 'Download'
92
+ })
93
+
94
+ # Make the request to download the PDF
95
+ headers = {
96
+ 'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36',
97
+ 'Content-Type': 'application/x-www-form-urlencoded',
98
+ 'Referer': 'https://www.mazegenerator.net/'
99
+ }
100
+
101
+ # Determine the full URL for the form submission
102
+ if form_action:
103
+ submit_url = urljoin('https://www.mazegenerator.net/', form_action)
104
+ else:
105
+ submit_url = 'https://www.mazegenerator.net/'
106
+
107
+ response = requests.post(submit_url, data=form_data, cookies=cookies, headers=headers)
108
+
109
+ # Check if the response is a PDF file
110
+ content_type = response.headers.get('content-type', '').lower()
111
+ if 'pdf' in content_type:
112
+ with open(filename, 'wb') as f:
113
+ f.write(response.content)
114
+ print(f"PDF downloaded successfully as {filename}")
115
+ return True
116
+ else:
117
+ print(f"Response is not a PDF. Content-Type: {content_type}")
118
+ # Save the response for debugging
119
+ with open("download_response.html", "w", encoding="utf-8") as f:
120
+ f.write(response.text)
121
+ print("Download response saved to download_response.html for inspection")
122
+ return False
123
+
124
+ def get_unique_filename(base_path):
125
+ """Generate a unique filename by incrementing the sequence number if file exists"""
126
+ if not os.path.exists(base_path):
127
+ return base_path
128
+
129
+ # Extract directory, filename, and extension
130
+ directory = os.path.dirname(base_path)
131
+ filename = os.path.basename(base_path)
132
+
133
+ # Parse the base name to extract width, height, and current number
134
+ # Expected format: maze_{width}x{height}_{number}.pdf
135
+ match = re.match(r'maze_(\d+)x(\d+)_(\d+)$', os.path.splitext(filename)[0])
136
+ if not match:
137
+ # If parsing fails, fall back to suffix method
138
+ name, ext = os.path.splitext(filename)
139
+ counter = 1
140
+ while True:
141
+ new_filename = f"{name}_{counter}{ext}"
142
+ new_path = os.path.join(directory, new_filename)
143
+ if not os.path.exists(new_path):
144
+ return new_path
145
+ counter += 1
146
+
147
+ width, height, current_num = match.groups()
148
+
149
+ # Find the next available number
150
+ next_num = int(current_num) + 1
151
+ while True:
152
+ new_filename = f"maze_{width}x{height}_{next_num}.pdf"
153
+ new_path = os.path.join(directory, new_filename)
154
+ if not os.path.exists(new_path):
155
+ return new_path
156
+ next_num += 1
157
+
158
+ def extract_first_page(input_pdf_path, output_pdf_path):
159
+ """Extract only the first page from a PDF file"""
160
+ try:
161
+ with open(input_pdf_path, 'rb') as input_file:
162
+ reader = PdfReader(input_file)
163
+ writer = PdfWriter()
164
+
165
+ # Add only the first page
166
+ if len(reader.pages) > 0:
167
+ writer.add_page(reader.pages[0])
168
+
169
+ # Get unique filename for output
170
+ unique_output_path = get_unique_filename(output_pdf_path)
171
+
172
+ with open(unique_output_path, 'wb') as output_file:
173
+ writer.write(output_file)
174
+
175
+ print(f"Extracted first page to {unique_output_path}")
176
+ return unique_output_path
177
+ else:
178
+ print("No pages found in the PDF")
179
+ return False
180
+
181
+ except Exception as e:
182
+ print(f"Error extracting first page: {e}")
183
+ return False
184
+
185
+ def generate_multiple_mazes(num_files, width, height):
186
+ """Generate multiple maze PDFs with specified dimensions"""
187
+ # Create pdfs directory if it doesn't exist
188
+ os.makedirs('pdfs', exist_ok=True)
189
+
190
+ success_count = 0
191
+
192
+ for i in range(num_files):
193
+ print(f"\nGenerating maze {i+1}/{num_files}...")
194
+
195
+ # Generate the maze
196
+ maze_html, maze_cookies = generate_maze(width, height)
197
+
198
+ # Download the full PDF
199
+ temp_filename = f"temp_maze_{i+1}.pdf"
200
+ if download_pdf(maze_html, maze_cookies, temp_filename):
201
+ # Extract only the first page to pdfs directory
202
+ # Start with i+1, but get_unique_filename will find the next available number
203
+ final_filename = os.path.join('pdfs', f"maze_{width}x{height}_{i+1}.pdf")
204
+ result_path = extract_first_page(temp_filename, final_filename)
205
+
206
+ if result_path:
207
+ success_count += 1
208
+ else:
209
+ print(f"Failed to extract first page for maze {i+1}")
210
+
211
+ # Clean up temp file
212
+ if os.path.exists(temp_filename):
213
+ os.remove(temp_filename)
214
+ else:
215
+ print(f"Failed to download maze {i+1}")
216
+
217
+ print(f"\nSuccessfully generated {success_count}/{num_files} maze PDFs in pdfs/ directory")
218
+ return success_count
219
+
220
+ def merge_pdfs(pdf_directory, output_filename="merged_mazes.pdf"):
221
+ """Merge all PDFs in directory into a single file and delete individual files"""
222
+ try:
223
+ # Get all PDF files in directory
224
+ pdf_files = [f for f in os.listdir(pdf_directory) if f.endswith('.pdf')]
225
+ pdf_files.sort() # Sort files by name
226
+
227
+ if not pdf_files:
228
+ print("No PDF files found to merge")
229
+ return False
230
+
231
+ print(f"Found {len(pdf_files)} PDF files to merge")
232
+
233
+ # Create PDF writer
234
+ writer = PdfWriter()
235
+
236
+ # Add each PDF to the writer
237
+ for pdf_file in pdf_files:
238
+ pdf_path = os.path.join(pdf_directory, pdf_file)
239
+ try:
240
+ with open(pdf_path, 'rb') as f:
241
+ reader = PdfReader(f)
242
+ for page in reader.pages:
243
+ writer.add_page(page)
244
+ print(f"Added {pdf_file} to merge")
245
+ except Exception as e:
246
+ print(f"Error reading {pdf_file}: {e}")
247
+ continue
248
+
249
+ # Write merged PDF
250
+ output_path = os.path.join(pdf_directory, output_filename)
251
+ with open(output_path, 'wb') as f:
252
+ writer.write(f)
253
+
254
+ print(f"Successfully merged {len(pdf_files)} PDFs into {output_path}")
255
+
256
+ # Delete individual PDF files
257
+ deleted_count = 0
258
+ for pdf_file in pdf_files:
259
+ pdf_path = os.path.join(pdf_directory, pdf_file)
260
+ try:
261
+ os.remove(pdf_path)
262
+ deleted_count += 1
263
+ print(f"Deleted {pdf_file}")
264
+ except Exception as e:
265
+ print(f"Error deleting {pdf_file}: {e}")
266
+
267
+ print(f"Deleted {deleted_count} individual PDF files")
268
+ return True
269
+
270
+ except Exception as e:
271
+ print(f"Error merging PDFs: {e}")
272
+ return False
273
+
274
+ def main():
275
+ parser = argparse.ArgumentParser(description='Generate maze PDFs with specified dimensions')
276
+ parser.add_argument('-n', '--number', type=int, default=1, help='Number of PDF files to generate (default: 1)')
277
+ parser.add_argument('-w', '--width', type=int, default=25, help='Maze width (default: 25)')
278
+ parser.add_argument('--height', type=int, default=35, help='Maze height (default: 35)')
279
+ parser.add_argument('--merge', action='store_true', help='Merge all existing PDFs in pdfs/ directory into a single file and delete individual files')
280
+
281
+ args = parser.parse_args()
282
+
283
+ # Handle merge mode
284
+ if args.merge:
285
+ print("Merge mode: Merging all existing PDFs in pdfs/ directory")
286
+ if os.path.exists('pdfs'):
287
+ success = merge_pdfs('pdfs')
288
+ return 0 if success else 1
289
+ else:
290
+ print("Error: pdfs/ directory does not exist")
291
+ return 1
292
+
293
+ # Handle generation mode
294
+ print(f"Generating {args.number} maze(s) with width={args.width}, height={args.height}")
295
+
296
+ # Validate parameters
297
+ if args.number < 1:
298
+ print("Error: Number of files must be at least 1")
299
+ return 1
300
+
301
+ if not (2 <= args.width <= 200):
302
+ print("Error: Width must be between 2 and 200")
303
+ return 1
304
+
305
+ if not (2 <= args.height <= 200):
306
+ print("Error: Height must be between 2 and 200")
307
+ return 1
308
+
309
+ # Generate the mazes
310
+ success_count = generate_multiple_mazes(args.number, args.width, args.height)
311
+
312
+ if success_count == args.number:
313
+ print("All mazes generated successfully!")
314
+ return 0
315
+ else:
316
+ print(f"Warning: Only {success_count} out of {args.number} mazes were generated successfully")
317
+ return 1
318
+
319
+ if __name__ == "__main__":
320
+ exit(main())
maze_generator.py ADDED
@@ -0,0 +1,136 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ import requests
3
+ import re
4
+ from urllib.parse import urljoin
5
+ import os
6
+
7
+ def get_maze_generator_page():
8
+ """Get the maze generator page to extract form data and session info"""
9
+ url = "https://www.mazegenerator.net/"
10
+ headers = {
11
+ 'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36'
12
+ }
13
+
14
+ response = requests.get(url, headers=headers)
15
+ return response.text, response.cookies
16
+
17
+ def extract_form_data(html_content):
18
+ """Extract form data and hidden fields from the page"""
19
+ # Look for form action URL
20
+ form_action_match = re.search(r'<form[^>]*action=["\']([^"\']*)["\']', html_content, re.IGNORECASE)
21
+ form_action = form_action_match.group(1) if form_action_match else ""
22
+
23
+ # Look for hidden inputs
24
+ hidden_inputs = re.findall(r'<input[^>]*type=["\']hidden["\'][^>]*name=["\']([^"\']*)["\'][^>]*value=["\']([^"\']*)["\']', html_content, re.IGNORECASE)
25
+ form_data = dict(hidden_inputs)
26
+
27
+ # Look for viewstate and other common ASP.NET fields
28
+ viewstate_match = re.search(r'name=["\']__VIEWSTATE["\'][^>]*value=["\']([^"\']*)["\']', html_content)
29
+ if viewstate_match:
30
+ form_data['__VIEWSTATE'] = viewstate_match.group(1)
31
+
32
+ viewstategen_match = re.search(r'name=["\']__VIEWSTATEGENERATOR["\'][^>]*value=["\']([^"\']*)["\']', html_content)
33
+ if viewstategen_match:
34
+ form_data['__VIEWSTATEGENERATOR'] = viewstategen_match.group(1)
35
+
36
+ eventvalidation_match = re.search(r'name=["\']__EVENTVALIDATION["\'][^>]*value=["\']([^"\']*)["\']', html_content)
37
+ if eventvalidation_match:
38
+ form_data['__EVENTVALIDATION'] = eventvalidation_match.group(1)
39
+
40
+ return form_action, form_data
41
+
42
+ def generate_maze(width=25, height=35):
43
+ """Generate a maze with specified dimensions"""
44
+ # Get the initial page
45
+ html_content, cookies = get_maze_generator_page()
46
+
47
+ # Extract form data
48
+ form_action, form_data = extract_form_data(html_content)
49
+
50
+ # Add maze parameters with correct field names from the HTML
51
+ form_data.update({
52
+ 'ShapeDropDownList': '1', # Rectangular
53
+ 'S1TesselationDropDownList': '1', # Orthogonal (Square cells)
54
+ 'S1WidthTextBox': str(width),
55
+ 'S1HeightTextBox': str(height),
56
+ 'S1InnerWidthTextBox': '0',
57
+ 'S1InnerHeightTextBox': '0',
58
+ 'S1StartsAtDropDownList': '1', # Top
59
+ 'AlgorithmParameter1TextBox': '50', # E parameter
60
+ 'AlgorithmParameter2TextBox': '100', # R parameter
61
+ 'GenerateButton': 'Generate'
62
+ })
63
+
64
+ # Make the request to generate the maze
65
+ headers = {
66
+ 'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36',
67
+ 'Content-Type': 'application/x-www-form-urlencoded',
68
+ 'Referer': 'https://www.mazegenerator.net/'
69
+ }
70
+
71
+ # Determine the full URL for the form submission
72
+ if form_action:
73
+ submit_url = urljoin('https://www.mazegenerator.net/', form_action)
74
+ else:
75
+ submit_url = 'https://www.mazegenerator.net/'
76
+
77
+ response = requests.post(submit_url, data=form_data, cookies=cookies, headers=headers)
78
+
79
+ return response.text, response.cookies
80
+
81
+ def download_pdf(html_content, cookies, filename="maze.pdf"):
82
+ """Download the maze as PDF by submitting the download form"""
83
+ # Extract form data for the download request
84
+ form_action, form_data = extract_form_data(html_content)
85
+
86
+ # Add download parameters
87
+ form_data.update({
88
+ 'FileFormatSelectorList': '1', # PDF (A4 size)
89
+ 'DownloadFileButton': 'Download'
90
+ })
91
+
92
+ # Make the request to download the PDF
93
+ headers = {
94
+ 'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36',
95
+ 'Content-Type': 'application/x-www-form-urlencoded',
96
+ 'Referer': 'https://www.mazegenerator.net/'
97
+ }
98
+
99
+ # Determine the full URL for the form submission
100
+ if form_action:
101
+ submit_url = urljoin('https://www.mazegenerator.net/', form_action)
102
+ else:
103
+ submit_url = 'https://www.mazegenerator.net/'
104
+
105
+ response = requests.post(submit_url, data=form_data, cookies=cookies, headers=headers)
106
+
107
+ # Check if the response is a PDF file
108
+ content_type = response.headers.get('content-type', '').lower()
109
+ if 'pdf' in content_type:
110
+ with open(filename, 'wb') as f:
111
+ f.write(response.content)
112
+ print(f"PDF downloaded successfully as {filename}")
113
+ return True
114
+ else:
115
+ print(f"Response is not a PDF. Content-Type: {content_type}")
116
+ # Save the response for debugging
117
+ with open("download_response.html", "w", encoding="utf-8") as f:
118
+ f.write(response.text)
119
+ print("Download response saved to download_response.html for inspection")
120
+ return False
121
+
122
+ if __name__ == "__main__":
123
+ print("Generating maze with width=25, height=35...")
124
+
125
+ # Generate the maze
126
+ maze_html, maze_cookies = generate_maze(25, 35)
127
+
128
+ # Try to download PDF
129
+ success = download_pdf(maze_html, maze_cookies, "maze_25x35.pdf")
130
+
131
+ if not success:
132
+ print("Let's try to find alternative download methods...")
133
+ # Save the HTML response for inspection
134
+ with open("maze_response.html", "w", encoding="utf-8") as f:
135
+ f.write(maze_html)
136
+ print("Response saved to maze_response.html for inspection")
requirements.txt ADDED
@@ -0,0 +1,4 @@
 
 
 
 
 
1
+ gradio==4.44.0
2
+ pandas==2.2.2
3
+ pypdf==3.17.4
4
+ requests==2.32.3