6363cur_sample = 0 # Current selected sample.
6464frame_times = [time .perf_counter ()]
6565frame_length = [0.0 ]
66+ START_TIME = time .perf_counter ()
67+
68+
69+ def _get_elapsed_time () -> float :
70+ """Return time passed since the start of the program."""
71+ return time .perf_counter () - START_TIME
6672
6773
6874class Sample (tcod .event .EventDispatch [None ]):
@@ -207,13 +213,13 @@ def __init__(self) -> None:
207213 )
208214
209215 def on_enter (self ) -> None :
210- self .counter = time . perf_counter ()
216+ self .counter = _get_elapsed_time ()
211217 # get a "screenshot" of the current sample screen
212218 sample_console .blit (dest = self .screenshot )
213219
214220 def on_draw (self ) -> None :
215- if time . perf_counter () - self .counter >= 1 :
216- self .counter = time . perf_counter ()
221+ if _get_elapsed_time () - self .counter >= 1 :
222+ self .counter = _get_elapsed_time ()
217223 self .x += self .x_dir
218224 self .y += self .y_dir
219225 if self .x == sample_console .width / 2 + 5 :
@@ -396,8 +402,8 @@ def get_noise(self) -> tcod.noise.Noise:
396402 )
397403
398404 def on_draw (self ) -> None :
399- self .dx = time . perf_counter () * 0.25
400- self .dy = time . perf_counter () * 0.25
405+ self .dx = _get_elapsed_time () * 0.25
406+ self .dy = _get_elapsed_time () * 0.25
401407 for y in range (2 * sample_console .height ):
402408 for x in range (2 * sample_console .width ):
403409 f = [
@@ -518,7 +524,7 @@ def ev_keydown(self, event: tcod.event.KeyDown) -> None:
518524 "##############################################" ,
519525)
520526
521- SAMPLE_MAP : NDArray [Any ] = np .array ([list ( line ) for line in SAMPLE_MAP_ ]).transpose ()
527+ SAMPLE_MAP : NDArray [Any ] = np .array ([[ ord ( c ) for c in line ] for line in SAMPLE_MAP_ ]).transpose ()
522528
523529FOV_ALGO_NAMES = (
524530 "BASIC " ,
@@ -555,17 +561,17 @@ def __init__(self) -> None:
555561 map_shape = (SAMPLE_SCREEN_WIDTH , SAMPLE_SCREEN_HEIGHT )
556562
557563 self .walkable : NDArray [np .bool_ ] = np .zeros (map_shape , dtype = bool , order = "F" )
558- self .walkable [:] = SAMPLE_MAP [:] == " "
564+ self .walkable [:] = SAMPLE_MAP [:] == ord ( " " )
559565
560566 self .transparent : NDArray [np .bool_ ] = np .zeros (map_shape , dtype = bool , order = "F" )
561- self .transparent [:] = self .walkable [:] | (SAMPLE_MAP == "=" )
567+ self .transparent [:] = self .walkable [:] | (SAMPLE_MAP [:] == ord ( "=" ) )
562568
563569 # Lit background colors for the map.
564570 self .light_map_bg : NDArray [np .uint8 ] = np .full (SAMPLE_MAP .shape , LIGHT_GROUND , dtype = "3B" )
565- self .light_map_bg [SAMPLE_MAP [:] == "#" ] = LIGHT_WALL
571+ self .light_map_bg [SAMPLE_MAP [:] == ord ( "#" ) ] = LIGHT_WALL
566572 # Dark background colors for the map.
567573 self .dark_map_bg : NDArray [np .uint8 ] = np .full (SAMPLE_MAP .shape , DARK_GROUND , dtype = "3B" )
568- self .dark_map_bg [SAMPLE_MAP [:] == "#" ] = DARK_WALL
574+ self .dark_map_bg [SAMPLE_MAP [:] == ord ( "#" ) ] = DARK_WALL
569575
570576 def draw_ui (self ) -> None :
571577 sample_console .print (
@@ -586,8 +592,8 @@ def on_draw(self) -> None:
586592 self .draw_ui ()
587593 sample_console .print (self .player_x , self .player_y , "@" )
588594 # Draw windows.
589- sample_console .rgb ["ch" ][SAMPLE_MAP == "=" ] = 0x2550 # BOX DRAWINGS DOUBLE HORIZONTAL
590- sample_console .rgb ["fg" ][SAMPLE_MAP == "=" ] = BLACK
595+ sample_console .rgb ["ch" ][SAMPLE_MAP [:] == ord ( "=" ) ] = 0x2550 # BOX DRAWINGS DOUBLE HORIZONTAL
596+ sample_console .rgb ["fg" ][SAMPLE_MAP [:] == ord ( "=" ) ] = BLACK
591597
592598 # Get a 2D boolean array of visible cells.
593599 fov = tcod .map .compute_fov (
@@ -600,7 +606,7 @@ def on_draw(self) -> None:
600606
601607 if self .torch :
602608 # Derive the touch from noise based on the current time.
603- torch_t = time . perf_counter () * 5
609+ torch_t = _get_elapsed_time () * 5
604610 # Randomize the light position between -1.5 and 1.5
605611 torch_x = self .player_x + self .noise .get_point (torch_t ) * 1.5
606612 torch_y = self .player_y + self .noise .get_point (torch_t + 11 ) * 1.5
@@ -632,7 +638,11 @@ def on_draw(self) -> None:
632638 # Linear interpolation between colors.
633639 sample_console .rgb ["bg" ] = dark_bg + (light_bg - dark_bg ) * light [..., np .newaxis ]
634640 else :
635- sample_console .bg [...] = np .where (fov [:, :, np .newaxis ], self .light_map_bg , self .dark_map_bg )
641+ sample_console .bg [...] = np .select (
642+ condlist = [fov [:, :, np .newaxis ]],
643+ choicelist = [self .light_map_bg ],
644+ default = self .dark_map_bg ,
645+ )
636646
637647 def ev_keydown (self , event : tcod .event .KeyDown ) -> None :
638648 MOVE_KEYS = { # noqa: N806
@@ -665,174 +675,89 @@ def ev_keydown(self, event: tcod.event.KeyDown) -> None:
665675
666676class PathfindingSample (Sample ):
667677 def __init__ (self ) -> None :
678+ """Initialize this sample."""
668679 self .name = "Path finding"
669680
670- self .px = 20
671- self .py = 10
672- self .dx = 24
673- self .dy = 1
674- self .dijkstra_dist = 0.0
681+ self .player_x = 20
682+ self .player_y = 10
683+ self .dest_x = 24
684+ self .dest_y = 1
675685 self .using_astar = True
676- self .recalculate = False
677686 self .busy = 0.0
678- self .old_char = " "
687+ self .cost = SAMPLE_MAP .T [:] == ord (" " )
688+ self .graph = tcod .path .SimpleGraph (cost = self .cost , cardinal = 70 , diagonal = 99 )
689+ self .pathfinder = tcod .path .Pathfinder (graph = self .graph )
679690
680- self .map = tcod .map .Map (SAMPLE_SCREEN_WIDTH , SAMPLE_SCREEN_HEIGHT )
681- for y in range (SAMPLE_SCREEN_HEIGHT ):
682- for x in range (SAMPLE_SCREEN_WIDTH ):
683- if SAMPLE_MAP [x , y ] == " " :
684- # ground
685- self .map .walkable [y , x ] = True
686- self .map .transparent [y , x ] = True
687- elif SAMPLE_MAP [x , y ] == "=" :
688- # window
689- self .map .walkable [y , x ] = False
690- self .map .transparent [y , x ] = True
691- self .path = tcod .path .AStar (self .map )
692- self .dijkstra = tcod .path .Dijkstra (self .map )
691+ self .background_console = tcod .console .Console (SAMPLE_SCREEN_WIDTH , SAMPLE_SCREEN_HEIGHT )
692+
693+ # draw the dungeon
694+ self .background_console .rgb ["fg" ] = BLACK
695+ self .background_console .rgb ["bg" ] = DARK_GROUND
696+ self .background_console .rgb ["bg" ][SAMPLE_MAP .T [:] == ord ("#" )] = DARK_WALL
697+ self .background_console .rgb ["ch" ][SAMPLE_MAP .T [:] == ord ("=" )] = ord ("═" )
693698
694699 def on_enter (self ) -> None :
695- # we draw the foreground only the first time.
696- # during the player movement, only the @ is redrawn.
697- # the rest impacts only the background color
698- # draw the help text & player @
699- sample_console .clear ()
700- sample_console .ch [self .dx , self .dy ] = ord ("+" )
701- sample_console .fg [self .dx , self .dy ] = WHITE
702- sample_console .ch [self .px , self .py ] = ord ("@" )
703- sample_console .fg [self .px , self .py ] = WHITE
704- sample_console .print (
705- 1 ,
706- 1 ,
707- "IJKL / mouse :\n move destination\n TAB : A*/dijkstra" ,
708- fg = WHITE ,
709- bg = None ,
710- )
711- sample_console .print (1 , 4 , "Using : A*" , fg = WHITE , bg = None )
712- # draw windows
713- for y in range (SAMPLE_SCREEN_HEIGHT ):
714- for x in range (SAMPLE_SCREEN_WIDTH ):
715- if SAMPLE_MAP [x , y ] == "=" :
716- libtcodpy .console_put_char (sample_console , x , y , libtcodpy .CHAR_DHLINE , libtcodpy .BKGND_NONE )
717- self .recalculate = True
700+ """Do nothing."""
718701
719702 def on_draw (self ) -> None :
720- if self .recalculate :
721- if self .using_astar :
722- libtcodpy .path_compute (self .path , self .px , self .py , self .dx , self .dy )
723- else :
724- self .dijkstra_dist = 0.0
725- # compute dijkstra grid (distance from px,py)
726- libtcodpy .dijkstra_compute (self .dijkstra , self .px , self .py )
727- # get the maximum distance (needed for rendering)
728- for y in range (SAMPLE_SCREEN_HEIGHT ):
729- for x in range (SAMPLE_SCREEN_WIDTH ):
730- d = libtcodpy .dijkstra_get_distance (self .dijkstra , x , y )
731- self .dijkstra_dist = max (d , self .dijkstra_dist )
732- # compute path from px,py to dx,dy
733- libtcodpy .dijkstra_path_set (self .dijkstra , self .dx , self .dy )
734- self .recalculate = False
735- self .busy = 0.2
703+ """Recompute and render pathfinding."""
704+ self .pathfinder = tcod .path .Pathfinder (graph = self .graph )
705+ # self.pathfinder.clear() # Known issues, needs fixing # noqa: ERA001
706+ self .pathfinder .add_root ((self .player_y , self .player_x ))
707+
736708 # draw the dungeon
737- for y in range (SAMPLE_SCREEN_HEIGHT ):
738- for x in range (SAMPLE_SCREEN_WIDTH ):
739- if SAMPLE_MAP [x , y ] == "#" :
740- libtcodpy .console_set_char_background (sample_console , x , y , DARK_WALL , libtcodpy .BKGND_SET )
741- else :
742- libtcodpy .console_set_char_background (sample_console , x , y , DARK_GROUND , libtcodpy .BKGND_SET )
709+ self .background_console .blit (dest = sample_console )
710+
711+ sample_console .print (self .dest_x , self .dest_y , "+" , fg = WHITE )
712+ sample_console .print (self .player_x , self .player_y , "@" , fg = WHITE )
713+ sample_console .print (1 , 1 , "IJKL / mouse :\n move destination\n TAB : A*/dijkstra" , fg = WHITE , bg = None )
714+ sample_console .print (1 , 4 , "Using : A*" , fg = WHITE , bg = None )
715+
716+ if not self .using_astar :
717+ self .pathfinder .resolve (goal = None )
718+ reachable = self .pathfinder .distance != np .iinfo (self .pathfinder .distance .dtype ).max
719+
720+ # draw distance from player
721+ dijkstra_max_dist = float (self .pathfinder .distance [reachable ].max ())
722+ np .array (self .pathfinder .distance , copy = True , dtype = np .float32 )
723+ interpolate = self .pathfinder .distance [reachable ] * 0.9 / dijkstra_max_dist
724+ color_delta = (np .array (DARK_GROUND ) - np .array (LIGHT_GROUND )).astype (np .float32 )
725+ sample_console .rgb .T ["bg" ][reachable ] = np .array (LIGHT_GROUND ) + interpolate [:, np .newaxis ] * color_delta
726+
743727 # draw the path
744- if self .using_astar :
745- for i in range (libtcodpy .path_size (self .path )):
746- x , y = libtcodpy .path_get (self .path , i )
747- libtcodpy .console_set_char_background (sample_console , x , y , LIGHT_GROUND , libtcodpy .BKGND_SET )
748- else :
749- for y in range (SAMPLE_SCREEN_HEIGHT ):
750- for x in range (SAMPLE_SCREEN_WIDTH ):
751- if SAMPLE_MAP [x , y ] != "#" :
752- libtcodpy .console_set_char_background (
753- sample_console ,
754- x ,
755- y ,
756- libtcodpy .color_lerp ( # type: ignore[arg-type]
757- LIGHT_GROUND ,
758- DARK_GROUND ,
759- 0.9 * libtcodpy .dijkstra_get_distance (self .dijkstra , x , y ) / self .dijkstra_dist ,
760- ),
761- libtcodpy .BKGND_SET ,
762- )
763- for i in range (libtcodpy .dijkstra_size (self .dijkstra )):
764- x , y = libtcodpy .dijkstra_get (self .dijkstra , i )
765- libtcodpy .console_set_char_background (sample_console , x , y , LIGHT_GROUND , libtcodpy .BKGND_SET )
728+ path = self .pathfinder .path_to ((self .dest_y , self .dest_x ))[1 :, ::- 1 ]
729+ sample_console .rgb ["bg" ][tuple (path .T )] = LIGHT_GROUND
766730
767731 # move the creature
768732 self .busy -= frame_length [- 1 ]
769733 if self .busy <= 0.0 :
770734 self .busy = 0.2
771- if self .using_astar :
772- if not libtcodpy .path_is_empty (self .path ):
773- libtcodpy .console_put_char (sample_console , self .px , self .py , " " , libtcodpy .BKGND_NONE )
774- self .px , self .py = libtcodpy .path_walk (self .path , True ) # type: ignore[assignment]
775- libtcodpy .console_put_char (sample_console , self .px , self .py , "@" , libtcodpy .BKGND_NONE )
776- elif not libtcodpy .dijkstra_is_empty (self .dijkstra ):
777- libtcodpy .console_put_char (sample_console , self .px , self .py , " " , libtcodpy .BKGND_NONE )
778- self .px , self .py = libtcodpy .dijkstra_path_walk (self .dijkstra ) # type: ignore[assignment]
779- libtcodpy .console_put_char (sample_console , self .px , self .py , "@" , libtcodpy .BKGND_NONE )
780- self .recalculate = True
735+ if len (path ):
736+ self .player_x = int (path .item (0 , 0 ))
737+ self .player_y = int (path .item (0 , 1 ))
781738
782739 def ev_keydown (self , event : tcod .event .KeyDown ) -> None :
783- if event .sym == tcod .event .KeySym .i and self .dy > 0 :
784- # destination move north
785- libtcodpy .console_put_char (sample_console , self .dx , self .dy , self .old_char , libtcodpy .BKGND_NONE )
786- self .dy -= 1
787- self .old_char = sample_console .ch [self .dx , self .dy ]
788- libtcodpy .console_put_char (sample_console , self .dx , self .dy , "+" , libtcodpy .BKGND_NONE )
789- if SAMPLE_MAP [self .dx , self .dy ] == " " :
790- self .recalculate = True
791- elif event .sym == tcod .event .KeySym .k and self .dy < SAMPLE_SCREEN_HEIGHT - 1 :
792- # destination move south
793- libtcodpy .console_put_char (sample_console , self .dx , self .dy , self .old_char , libtcodpy .BKGND_NONE )
794- self .dy += 1
795- self .old_char = sample_console .ch [self .dx , self .dy ]
796- libtcodpy .console_put_char (sample_console , self .dx , self .dy , "+" , libtcodpy .BKGND_NONE )
797- if SAMPLE_MAP [self .dx , self .dy ] == " " :
798- self .recalculate = True
799- elif event .sym == tcod .event .KeySym .j and self .dx > 0 :
800- # destination move west
801- libtcodpy .console_put_char (sample_console , self .dx , self .dy , self .old_char , libtcodpy .BKGND_NONE )
802- self .dx -= 1
803- self .old_char = sample_console .ch [self .dx , self .dy ]
804- libtcodpy .console_put_char (sample_console , self .dx , self .dy , "+" , libtcodpy .BKGND_NONE )
805- if SAMPLE_MAP [self .dx , self .dy ] == " " :
806- self .recalculate = True
807- elif event .sym == tcod .event .KeySym .l and self .dx < SAMPLE_SCREEN_WIDTH - 1 :
808- # destination move east
809- libtcodpy .console_put_char (sample_console , self .dx , self .dy , self .old_char , libtcodpy .BKGND_NONE )
810- self .dx += 1
811- self .old_char = sample_console .ch [self .dx , self .dy ]
812- libtcodpy .console_put_char (sample_console , self .dx , self .dy , "+" , libtcodpy .BKGND_NONE )
813- if SAMPLE_MAP [self .dx , self .dy ] == " " :
814- self .recalculate = True
740+ """Handle movement and UI."""
741+ if event .sym == tcod .event .KeySym .i and self .dest_y > 0 : # destination move north
742+ self .dest_y -= 1
743+ elif event .sym == tcod .event .KeySym .k and self .dest_y < SAMPLE_SCREEN_HEIGHT - 1 : # destination move south
744+ self .dest_y += 1
745+ elif event .sym == tcod .event .KeySym .j and self .dest_x > 0 : # destination move west
746+ self .dest_x -= 1
747+ elif event .sym == tcod .event .KeySym .l and self .dest_x < SAMPLE_SCREEN_WIDTH - 1 : # destination move east
748+ self .dest_x += 1
815749 elif event .sym == tcod .event .KeySym .TAB :
816750 self .using_astar = not self .using_astar
817- if self .using_astar :
818- libtcodpy .console_print (sample_console , 1 , 4 , "Using : A* " )
819- else :
820- libtcodpy .console_print (sample_console , 1 , 4 , "Using : Dijkstra" )
821- self .recalculate = True
822751 else :
823752 super ().ev_keydown (event )
824753
825754 def ev_mousemotion (self , event : tcod .event .MouseMotion ) -> None :
755+ """Move destination via mouseover."""
826756 mx = event .tile .x - SAMPLE_SCREEN_X
827757 my = event .tile .y - SAMPLE_SCREEN_Y
828- if 0 <= mx < SAMPLE_SCREEN_WIDTH and 0 <= my < SAMPLE_SCREEN_HEIGHT and (self .dx != mx or self .dy != my ):
829- libtcodpy .console_put_char (sample_console , self .dx , self .dy , self .old_char , libtcodpy .BKGND_NONE )
830- self .dx = mx
831- self .dy = my
832- self .old_char = sample_console .ch [self .dx , self .dy ]
833- libtcodpy .console_put_char (sample_console , self .dx , self .dy , "+" , libtcodpy .BKGND_NONE )
834- if SAMPLE_MAP [self .dx , self .dy ] == " " :
835- self .recalculate = True
758+ if 0 <= mx < SAMPLE_SCREEN_WIDTH and 0 <= my < SAMPLE_SCREEN_HEIGHT :
759+ self .dest_x = mx
760+ self .dest_y = my
836761
837762
838763#############################################
@@ -1044,7 +969,7 @@ def on_draw(self) -> None:
1044969 y = sample_console .height / 2
1045970 scalex = 0.2 + 1.8 * (1.0 + math .cos (time .time () / 2 )) / 2.0
1046971 scaley = scalex
1047- angle = time . perf_counter ()
972+ angle = _get_elapsed_time ()
1048973 if int (time .time ()) % 2 :
1049974 # split the color channels of circle.png
1050975 # the red channel
@@ -1529,7 +1454,7 @@ def draw_stats() -> None:
15291454 root_console .print (
15301455 root_console .width ,
15311456 47 ,
1532- f"elapsed : { int (time . perf_counter () * 1000 ):8d} ms { time . perf_counter ():5.2f} s" ,
1457+ f"elapsed : { int (_get_elapsed_time () * 1000 ):8d} ms { _get_elapsed_time ():5.2f} s" ,
15331458 fg = GREY ,
15341459 alignment = libtcodpy .RIGHT ,
15351460 )
0 commit comments