11#
2- # Copyright (C) 2012-2022 Sébastien Helleu <flashcode@flashtux.org>
2+ # SPDX-FileCopyrightText: 2012-2026 Sébastien Helleu <flashcode@flashtux.org>
3+ #
4+ # SPDX-License-Identifier: GPL-3.0-or-later
35#
46# This program is free software; you can redistribute it and/or modify
57# it under the terms of the GNU General Public License as published by
1517# along with this program. If not, see <http://www.gnu.org/licenses/>.
1618#
1719
18- #
19- # Interactive maze generator and solver for WeeChat.
20- #
21- # History:
22- #
23- # 2022-02-20, Sébastien Helleu <flashcode@flashtux.org>:
24- # version 1.0.0: first public version
25- # 2012-10-10, Sébastien Helleu <flashcode@flashtux.org>:
26- # version 0.0.1: initial release
27- #
28-
29- """Maze generator and solver for WeeChat."""
30-
31- from dataclasses import dataclass , field
32- from typing import ClassVar , Dict , List , Optional , Tuple
20+ """Interactive maze generator and solver for WeeChat."""
3321
3422import random
23+ from dataclasses import dataclass , field
3524
3625try :
37- import weechat
26+ import weechat # type: ignore[import]
27+
3828 IMPORT_OK = True
3929except ImportError :
4030 print ("This script must be run under WeeChat." )
4333
4434SCRIPT_NAME = "maze"
4535SCRIPT_AUTHOR = "Sébastien Helleu <flashcode@flashtux.org>"
46- SCRIPT_VERSION = "1.0.0 "
36+ SCRIPT_VERSION = "1.0.1 "
4737SCRIPT_LICENSE = "GPL3"
4838SCRIPT_DESC = "Interactive maze generator and solver"
4939
5040SCRIPT_COMMAND = "maze"
5141
52- MAZE_KEYS : Dict [str , Tuple [str , str ]] = {
42+ MAZE_KEYS : dict [str , tuple [str , str ]] = {
5343 "n" : ("" , "new maze" ),
5444 "d" : ("default" , "default size" ),
5545 "+" : ("+" , "bigger" ),
6353@dataclass
6454class Maze :
6555 """Maze."""
56+
6657 width : int = 0
6758 height : int = 0
6859
69- cells : List [int ] = field (default_factory = list , init = False )
70- solution : List [tuple [int , int , bool ]] = field (default_factory = list ,
71- init = False )
60+ cells : list [int ] = field (default_factory = list , init = False )
61+ solution : list [tuple [int , int , bool ]] = field (default_factory = list , init = False )
7262 buffer : str = field (init = False )
7363 timer : str = field (default = "" , init = False )
7464
7565 # cell status
76- VISITED : ClassVar [ int ] = 1 # visited cell
77- TOP : ClassVar [ int ] = 2 # door opened on top
78- BOTTOM : ClassVar [ int ] = 4 # door opened on bottom
79- LEFT : ClassVar [ int ] = 8 # door opened on the left
80- RIGHT : ClassVar [ int ] = 16 # door opened on the right
81- SOLUTION : ClassVar [ int ] = 32 # cell part of the solution
82- SOLUTION_INT : ClassVar [ int ] = 64 # cell displayed for solution
66+ VISITED : int = 1 # visited cell
67+ TOP : int = 2 # door opened on top
68+ BOTTOM : int = 4 # door opened on bottom
69+ LEFT : int = 8 # door opened on the left
70+ RIGHT : int = 16 # door opened on the right
71+ SOLUTION : int = 32 # cell part of the solution
72+ SOLUTION_INT : int = 64 # cell displayed for solution
8373
8474 def __post_init__ (self ) -> None :
8575 """Initialize maze with the given size."""
@@ -89,10 +79,9 @@ def __post_init__(self) -> None:
8979 self .solution = []
9080 self .buffer = Maze .open_buffer ()
9181 if self .buffer :
92- keys = ", " .join ([
93- f"alt+\" { key } \" : { value [1 ]} "
94- for key , value in MAZE_KEYS .items ()
95- ])
82+ keys = ", " .join (
83+ [f'alt+"{ key } ": { value [1 ]} ' for key , value in MAZE_KEYS .items ()]
84+ )
9685 weechat .buffer_set (
9786 self .buffer ,
9887 "title" ,
@@ -123,8 +112,8 @@ def open_buffer() -> str:
123112 return buf
124113
125114 def get_adjacent_cells (
126- self , col : int , line : int
127- ) -> List [ Tuple [int , int , int , int ]]:
115+ self , col : int , line : int
116+ ) -> list [ tuple [int , int , int , int ]]:
128117 """Get adjacent cells."""
129118 list_cells = []
130119 if col < self .width - 1 : # right
@@ -144,11 +133,11 @@ def generate(self) -> None:
144133 col : int = 0
145134 line : int = 0
146135 self .cells [0 ] |= self .VISITED
147- stack : List [ Tuple [int , int ]] = []
136+ stack : list [ tuple [int , int ]] = []
148137 while True :
149138 pos : int = (line * self .width ) + col
150- list_cells : List [ Tuple [int , int , int , int ]] = (
151- self . get_adjacent_cells ( col , line )
139+ list_cells : list [ tuple [int , int , int , int ]] = self . get_adjacent_cells (
140+ col , line
152141 )
153142 random .shuffle (list_cells )
154143 for col2 , line2 , to_neighbor , from_neighbor in list_cells :
@@ -171,25 +160,28 @@ def solve(self, interactive: bool = False) -> None:
171160 """Solve a maze: find path from entry (0,0) to exit (n,n)."""
172161 self .remove_timer ()
173162 for index , cell in enumerate (self .cells ):
174- self .cells [index ] = cell & ~ (self .VISITED | self .SOLUTION
175- | self .SOLUTION_INT )
163+ self .cells [index ] = cell & ~ (
164+ self .VISITED | self .SOLUTION | self .SOLUTION_INT
165+ )
176166 col : int = 0
177167 line : int = 0
178168 self .cells [0 ] |= self .SOLUTION
179169 self .solution = [(col , line , True )]
180- stack : List [ Tuple [int , int ]] = []
170+ stack : list [ tuple [int , int ]] = []
181171 visited_solution = self .VISITED | self .SOLUTION
182172 while not self .cells [- 1 ] & self .SOLUTION :
183173 pos : int = (line * self .width ) + col
184174 self .cells [pos ] |= self .VISITED
185- list_cells : List [ Tuple [int , int , int , int ]] = (
186- self . get_adjacent_cells ( col , line )
175+ list_cells : list [ tuple [int , int , int , int ]] = self . get_adjacent_cells (
176+ col , line
187177 )
188178 for col2 , line2 , to_neighbor , _ in list_cells :
189179 pos2 : int = (line2 * self .width ) + col2
190180 # door opened and neighbor not visited/solution?
191- if self .cells [pos ] & to_neighbor \
192- and not self .cells [pos2 ] & visited_solution :
181+ if (
182+ self .cells [pos ] & to_neighbor
183+ and not self .cells [pos2 ] & visited_solution
184+ ):
193185 self .cells [pos2 ] |= visited_solution
194186 self .solution .append ((col2 , line2 , True ))
195187 stack .append ((col , line ))
@@ -217,27 +209,27 @@ def display_line(self, line: int) -> None:
217209 """Display a line of maze."""
218210 str_line : str
219211 if line >= self .height :
220- str_line = (
221- weechat .color ("white" )
222- + ("▀" * ((self .width * 2 ) + 1 ))
223- )
212+ str_line = weechat .color ("white" ) + ("▀" * ((self .width * 2 ) + 1 ))
224213 else :
225214 both_sol : int = self .SOLUTION | self .SOLUTION_INT
226215 cell : int = self .cells [line * self .width ]
227216 color : str = (
228- "lightred" if cell & self .SOLUTION_INT
229- else "blue" if cell & self .SOLUTION
217+ "lightred"
218+ if cell & self .SOLUTION_INT
219+ else "blue"
220+ if cell & self .SOLUTION
230221 else "black"
231222 )
232- str_line = (
233- weechat .color (f"{ color } ,white" )
234- + ("▄" if cell & self .LEFT else " " )
223+ str_line = weechat .color (f"{ color } ,white" ) + (
224+ "▄" if cell & self .LEFT else " "
235225 )
236226 for col in range (self .width ):
237227 cell = self .cells [(line * self .width ) + col ]
238228 color = (
239- "lightred" if cell & self .SOLUTION_INT
240- else "blue" if cell & self .SOLUTION
229+ "lightred"
230+ if cell & self .SOLUTION_INT
231+ else "blue"
232+ if cell & self .SOLUTION
241233 else "black"
242234 )
243235 # door opened on top?
@@ -250,9 +242,8 @@ def display_line(self, line: int) -> None:
250242 else :
251243 str_line += weechat .color (f"{ color } ,white" ) + "▄"
252244 # door opened on the right?
253- str_line += (
254- weechat .color (f"{ color } ,white" )
255- + ("▄" if cell & self .RIGHT else " " )
245+ str_line += weechat .color (f"{ color } ,white" ) + (
246+ "▄" if cell & self .RIGHT else " "
256247 )
257248 str_line += weechat .color ("reset" )
258249 weechat .prnt_y (self .buffer , line , str_line )
@@ -278,9 +269,6 @@ def show_interactive_solution(self) -> None:
278269
279270 def show_next_solution (self ) -> None :
280271 """Show next solution step."""
281- col : int
282- line : int
283- show : bool
284272 col , line , show = self .solution .pop (0 )
285273 pos : int = (line * self .width ) + col
286274 if show :
@@ -296,10 +284,10 @@ def __del__(self) -> None:
296284 self .remove_timer ()
297285
298286
299- maze : Optional [ Maze ] = None
287+ maze : Maze | None = None
300288
301289
302- def maze_input_buffer (data : str , buffer : str , str_input : str ) -> int :
290+ def maze_input_buffer (_data : str , buffer : str , str_input : str ) -> int :
303291 """Input data in maze buffer."""
304292 # pylint: disable=unused-argument
305293 if str_input .lower () == "q" :
@@ -309,23 +297,23 @@ def maze_input_buffer(data: str, buffer: str, str_input: str) -> int:
309297 return weechat .WEECHAT_RC_OK
310298
311299
312- def maze_close_buffer (data : str , buffer : str ) -> int :
300+ def maze_close_buffer (_data : str , _buffer : str ) -> int :
313301 """Called when maze buffer is closed."""
314302 # pylint: disable=unused-argument
315303 global maze
316304 maze = None
317305 return weechat .WEECHAT_RC_OK
318306
319307
320- def maze_timer_cb (data : str , remaining_calls : int ) -> int :
308+ def maze_timer_cb (_data : str , _remaining_calls : int ) -> int :
321309 """Timer used to show solution, one cell by one cell."""
322310 global maze
323311 if maze :
324312 maze .show_next_solution ()
325313 return weechat .WEECHAT_RC_OK
326314
327315
328- def maze_get_size (args : str = "" ) -> Tuple [int , int ]:
316+ def maze_get_size (args : str = "" ) -> tuple [int , int ]:
329317 """Get maze size with args, defaulting to current maze or window size."""
330318 global maze
331319 width : int = 0
@@ -348,20 +336,20 @@ def maze_get_size(args: str = "") -> Tuple[int, int]:
348336 width , height = 0 , 0
349337 if not width or not height :
350338 # automatic size with size of window
351- win_width : int = weechat .window_get_integer (weechat .current_window (),
352- "win_chat_width" ) - 1
353- win_height : int = weechat .window_get_integer (weechat .current_window (),
354- "win_chat_height" ) - 1
339+ win_width : int = (
340+ weechat .window_get_integer (weechat .current_window (), "win_chat_width" ) - 1
341+ )
342+ win_height : int = (
343+ weechat .window_get_integer (weechat .current_window (), "win_chat_height" ) - 1
344+ )
355345 size : int = min (win_width , win_height )
356346 width , height = size , size
357347 return width , height
358348
359349
360- def maze_get_other_size (pct_diff : int ) -> Tuple [int , int ]:
350+ def maze_get_other_size (pct_diff : int ) -> tuple [int , int ]:
361351 """Get another size using a percent of size to add or subtract."""
362352 factor : int = pct_diff // abs (pct_diff )
363- width : int
364- height : int
365353 width , height = maze_get_size ()
366354 add_width : int = max (2 , (width * abs (pct_diff )) // 100 )
367355 add_height : int = max (2 , (height * abs (pct_diff )) // 100 )
@@ -378,11 +366,9 @@ def maze_new(width: int, height: int) -> None:
378366 maze .display ()
379367
380368
381- def maze_cmd_cb (data : str , buffer : str , args : str ) -> int :
369+ def maze_cmd_cb (_data : str , _buffer : str , args : str ) -> int :
382370 """The /maze command."""
383371 global maze
384- width : int
385- height : int
386372 if args in ("s" , "solve" ):
387373 if maze :
388374 maze .solve ()
@@ -403,21 +389,24 @@ def maze_cmd_cb(data: str, buffer: str, args: str) -> int:
403389 width , height = maze_get_size ()
404390 else :
405391 error = weechat .prefix ("error" )
406- weechat .prnt ("" , f" { error } maze error: unknown option \ "{ args } \" " )
392+ weechat .prnt ("" , f' { error } maze error: unknown option "{ args } "' )
407393 return weechat .WEECHAT_RC_OK
408394 maze_new (width , height )
409395 return weechat .WEECHAT_RC_OK
410396
411397
412- if __name__ == "__main__" and IMPORT_OK :
413- if weechat .register (SCRIPT_NAME , SCRIPT_AUTHOR , SCRIPT_VERSION ,
414- SCRIPT_LICENSE , SCRIPT_DESC , "" , "" ):
415- weechat .hook_command (
416- SCRIPT_COMMAND ,
417- "Generate and solve a maze." ,
418- "size || n|new || d|default || s|solve || i|isolve || r|reset "
419- "|| +|-" ,
420- """\
398+ if (
399+ __name__ == "__main__"
400+ and IMPORT_OK
401+ and weechat .register (
402+ SCRIPT_NAME , SCRIPT_AUTHOR , SCRIPT_VERSION , SCRIPT_LICENSE , SCRIPT_DESC , "" , ""
403+ )
404+ ):
405+ weechat .hook_command (
406+ SCRIPT_COMMAND ,
407+ "Generate and solve a maze." ,
408+ "size || n|new || d|default || s|solve || i|isolve || r|reset || +|-" ,
409+ """\
421410 size: one size (square) or width and height separated by spaces
422411 new: regenerate another maze
423412default: regenerate another maze with default size
@@ -429,7 +418,7 @@ def maze_cmd_cb(data: str, buffer: str, args: str) -> int:
429418
430419All options shown above can be given in input of maze buffer.
431420""" ,
432- "" ,
433- "maze_cmd_cb" ,
434- "" ,
435- )
421+ "" ,
422+ "maze_cmd_cb" ,
423+ "" ,
424+ )
0 commit comments