McRogueFace/roguelike_tutorial/rogueliketutorials.com/Part 12 - Increasing Diffic...

1036 lines
53 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!DOCTYPE html>
<html lang="en" style="color-scheme: dark;"><head>
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
<title>
Part 12 - Increasing Difficulty · Roguelike Tutorials
</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="color-scheme" content="light dark">
<meta name="description" content="Despite the fact that we can go down floors now, the dungeon doesnt get progressively more difficult as the player descends. This is because the method in which we place monsters and items is the same on each floor. In this chapter, well adjust how we place things in the dungeon, so things get more difficult with each floor.
Currently, we pass maximum_monsters and maximum_items into the place_entities function, and this number does not change.">
<meta name="keywords" content="">
<meta name="twitter:card" content="summary">
<meta name="twitter:title" content="Part 12 - Increasing Difficulty">
<meta name="twitter:description" content="Despite the fact that we can go down floors now, the dungeon doesnt get progressively more difficult as the player descends. This is because the method in which we place monsters and items is the same on each floor. In this chapter, well adjust how we place things in the dungeon, so things get more difficult with each floor.
Currently, we pass maximum_monsters and maximum_items into the place_entities function, and this number does not change.">
<meta property="og:title" content="Part 12 - Increasing Difficulty">
<meta property="og:description" content="Despite the fact that we can go down floors now, the dungeon doesnt get progressively more difficult as the player descends. This is because the method in which we place monsters and items is the same on each floor. In this chapter, well adjust how we place things in the dungeon, so things get more difficult with each floor.
Currently, we pass maximum_monsters and maximum_items into the place_entities function, and this number does not change.">
<meta property="og:type" content="article">
<meta property="og:url" content="https://rogueliketutorials.com/tutorials/tcod/v2/part-12/"><meta property="article:section" content="tutorials">
<meta property="article:published_time" content="2020-07-28T00:00:00+00:00">
<meta property="article:modified_time" content="2020-07-28T00:00:00+00:00">
<link rel="canonical" href="https://rogueliketutorials.com/tutorials/tcod/v2/part-12/">
<link rel="preload" href="https://rogueliketutorials.com/fonts/forkawesome-webfont.woff2?v=1.2.0" as="font" type="font/woff2" crossorigin="">
<link rel="stylesheet" href="Part%2012%20-%20Increasing%20Difficulty%20%C2%B7%20Roguelike%20Tutorials_files/coder.min.c4d7e93a158eda5a65b3df343745d2092a0a1e2170feeec909.css" integrity="sha256-xNfpOhWO2lpls980N0XSCSoKHiFw/u7JCbiolEOQPGo=" crossorigin="anonymous" media="screen">
<link rel="stylesheet" href="Part%2012%20-%20Increasing%20Difficulty%20%C2%B7%20Roguelike%20Tutorials_files/coder-dark.min.78b5fe3864945faf5207fb8fe3ab2320d49c3365def0e.css" integrity="sha256-eLX+OGSUX69SB/uP46sjINScM2Xe8OiKwd8N2txUoDw=" crossorigin="anonymous" media="screen">
<link rel="stylesheet" href="Part%2012%20-%20Increasing%20Difficulty%20%C2%B7%20Roguelike%20Tutorials_files/style.min.9d3eb202952dddb888856ff12c83bc88de866c596286bfb4c1.css" integrity="sha256-nT6yApUt3biIhW/xLIO8iN6GbFlihr+0wfjmvq2a42Y=" crossorigin="anonymous" media="screen">
<link rel="icon" type="image/png" href="https://rogueliketutorials.com/images/favicon-32x32.png" sizes="32x32">
<link rel="icon" type="image/png" href="https://rogueliketutorials.com/images/favicon-16x16.png" sizes="16x16">
<link rel="apple-touch-icon" href="https://rogueliketutorials.com/images/apple-touch-icon.png">
<link rel="apple-touch-icon" sizes="180x180" href="https://rogueliketutorials.com/images/apple-touch-icon.png">
<link rel="manifest" href="https://rogueliketutorials.com/site.webmanifest">
<link rel="mask-icon" href="https://rogueliketutorials.com/images/safari-pinned-tab.svg" color="#5bbad5">
<meta name="generator" content="Hugo 0.110.0">
<style>:is([id*='google_ads_iframe'],[id*='taboola-'],.taboolaHeight,.taboola-placeholder,#top-ad,#credential_picker_container,#credentials-picker-container,#credential_picker_iframe,[id*='google-one-tap-iframe'],#google-one-tap-popup-container,.google-one-tap__module,.google-one-tap-modal-div,#amp_floatingAdDiv,#ez-content-blocker-container) {display:none!important;min-height:0!important;height:0!important;}</style></head>
<body class="colorscheme-dark vsc-initialized">
<div class="float-container">
<a id="dark-mode-toggle" class="colorscheme-toggle">
<i class="fa fa-adjust fa-fw" aria-hidden="true"></i>
</a>
</div>
<main class="wrapper">
<nav class="navigation">
<section class="container">
<a class="navigation-title" href="https://rogueliketutorials.com/">
Roguelike Tutorials
</a>
<input type="checkbox" id="menu-toggle">
<label class="menu-button float-right" for="menu-toggle">
<i class="fa fa-bars fa-fw" aria-hidden="true"></i>
</label>
<ul class="navigation-list">
<li class="navigation-item">
<a class="navigation-link" href="https://rogueliketutorials.com/">Home</a>
</li>
<li class="navigation-item">
<a class="navigation-link" href="https://rogueliketutorials.com/tutorials/tcod/v2/">TCOD Tutorial (2020)</a>
</li>
<li class="navigation-item">
<a class="navigation-link" href="https://rogueliketutorials.com/tutorials/tcod/2019/">TCOD Tutorial (2019)</a>
</li>
<li class="navigation-item">
<a class="navigation-link" href="https://rogueliketutorials.com/about/">About</a>
</li>
</ul>
</section>
</nav>
<div class="content">
<section class="container page">
<article>
<header>
<h1 class="title">
<a class="title-link" href="https://rogueliketutorials.com/tutorials/tcod/v2/part-12/">
Part 12 - Increasing Difficulty
</a>
</h1>
</header>
<p>Despite the fact that we can go down floors now, the dungeon
doesnt get progressively more difficult as the player descends. This is
because the method in which we place monsters and items is the same on
each floor. In this chapter, well adjust how we place things in the
dungeon, so things get more difficult with each floor.</p>
<p>Currently, we pass <code>maximum_monsters</code> and <code>maximum_items</code> into the <code>place_entities</code>
function, and this number does not change. To adjust the difficulty of
our game, we can change these numbers based on the floor number. The way
well accomplish this is by setting up a list of tuples, which will
contain two integers: the floor number, and the number of
items/monsters.</p>
<p>Add the following to <code>procgen.py</code>:</p>
<div>
<button class="btn btn-primary data-toggle-tab active" data-toggle-tab="diff">
Diff
</button>
<button class="btn btn-secondary data-toggle-tab" data-toggle-tab="original">
Original
</button>
<div class="data-pane active" data-pane="diff">
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-diff" data-lang="diff"><span style="display:flex;"><span>...
</span></span><span style="display:flex;"><span>if TYPE_CHECKING:
</span></span><span style="display:flex;"><span> from engine import Engine
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">+max_items_by_floor = [
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e">+ (1, 1),
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e">+ (4, 2),
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e">+]
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e"></span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">+max_monsters_by_floor = [
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e">+ (1, 2),
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e">+ (4, 3),
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e">+ (6, 5),
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e">+]
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e"></span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>class RectangularRoom:
</span></span><span style="display:flex;"><span> ...
</span></span></code></pre></div>
</div>
<div class="data-pane" data-pane="original">
<pre>...
if TYPE_CHECKING:
from engine import Engine
<span class="new-text">max_items_by_floor = [
(1, 1),
(4, 2),
]
max_monsters_by_floor = [
(1, 2),
(4, 3),
(6, 5),
]</span>
class RectangularRoom:
...</pre>
</div>
</div>
<p>As mentioned, the first number in these tuples represents the floor
number, and the second represents the maximum of either the items or the
monsters.</p>
<p>You might be wondering why weve only supplied values for only
certain floors. Rather than having to type out each floor number, well
provide the floor numbers that have a different value, so that we can
loop through the list and stop when we hit a floor number higher than
the one were on. For example, if were on floor 3, well take the floor
1 entry for both items and monsters, and stop iteration when we reach
the second item in the list, since floor 4 is higher than floor 3.</p>
<p>Lets write the function to take care of this. Well call it <code>get_max_value_for_floor</code>, as were getting the maximum value for either the items or monsters. It looks like this:</p>
<div>
<button class="btn btn-primary data-toggle-tab active" data-toggle-tab="diff">
Diff
</button>
<button class="btn btn-secondary data-toggle-tab" data-toggle-tab="original">
Original
</button>
<div class="data-pane active" data-pane="diff">
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-diff" data-lang="diff"><span style="display:flex;"><span>...
</span></span><span style="display:flex;"><span>max_items_by_floor = [
</span></span><span style="display:flex;"><span> (1, 1),
</span></span><span style="display:flex;"><span> (4, 2),
</span></span><span style="display:flex;"><span>]
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>max_monsters_by_floor = [
</span></span><span style="display:flex;"><span> (1, 2),
</span></span><span style="display:flex;"><span> (4, 3),
</span></span><span style="display:flex;"><span> (6, 5),
</span></span><span style="display:flex;"><span>]
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">+def get_max_value_for_floor(
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e">+ weighted_chances_by_floor: List[Tuple[int, int]], floor: int
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e">+) -&gt; int:
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e">+ current_value = 0
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e"></span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">+ for floor_minimum, value in weighted_chances_by_floor:
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e">+ if floor_minimum &gt; floor:
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e">+ break
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e">+ else:
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e">+ current_value = value
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e"></span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">+ return current_value
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e"></span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>class RectangularRoom:
</span></span><span style="display:flex;"><span> ...
</span></span></code></pre></div>
</div>
<div class="data-pane" data-pane="original">
<pre>...
max_items_by_floor = [
(1, 1),
(4, 2),
]
max_monsters_by_floor = [
(1, 2),
(4, 3),
(6, 5),
]
<span class="new-text">def get_max_value_for_floor(
max_value_by_floor: List[Tuple[int, int]], floor: int
) -&gt; int:
current_value = 0
for floor_minimum, value in max_value_by_floor:
if floor_minimum &gt; floor:
break
else:
current_value = value
return current_value</span>
class RectangularRoom:
...</pre>
</div>
</div>
<p>Using this function is quite simple: we simply remove the <code>maximum_monsters</code> and <code>maximum_items</code> parameters from the <code>place_entities</code> function, pass the <code>floor_number</code> instead, and use that to get our maximum values from the <code>get_max_value_for_floor</code> function.</p>
<div>
<button class="btn btn-primary data-toggle-tab active" data-toggle-tab="diff">
Diff
</button>
<button class="btn btn-secondary data-toggle-tab" data-toggle-tab="original">
Original
</button>
<div class="data-pane active" data-pane="diff">
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-diff" data-lang="diff"><span style="display:flex;"><span><span style="color:#f92672">-def place_entities(
</span></span></span><span style="display:flex;"><span><span style="color:#f92672">- room: RectangularRoom, dungeon: GameMap, maximum_monsters: int, maximum_items: int
</span></span></span><span style="display:flex;"><span><span style="color:#f92672">-) -&gt; None:
</span></span></span><span style="display:flex;"><span><span style="color:#f92672">- number_of_monsters = random.randint(0, maximum_monsters)
</span></span></span><span style="display:flex;"><span><span style="color:#f92672">- number_of_items = random.randint(0, maximum_items)
</span></span></span><span style="display:flex;"><span><span style="color:#f92672"></span><span style="color:#a6e22e">+def place_entities(room: RectangularRoom, dungeon: GameMap, floor_number: int,) -&gt; None:
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e">+ number_of_monsters = random.randint(
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e">+ 0, get_max_value_for_floor(max_monsters_by_floor, floor_number)
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e">+ )
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e">+ number_of_items = random.randint(
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e">+ 0, get_max_value_for_floor(max_items_by_floor, floor_number)
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e">+ )
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e"></span>
</span></span><span style="display:flex;"><span> for i in range(number_of_monsters):
</span></span><span style="display:flex;"><span> ...
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>...
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>def generate_dungeon(
</span></span><span style="display:flex;"><span> max_rooms: int,
</span></span><span style="display:flex;"><span> room_min_size: int,
</span></span><span style="display:flex;"><span> room_max_size: int,
</span></span><span style="display:flex;"><span> map_width: int,
</span></span><span style="display:flex;"><span> map_height: int,
</span></span><span style="display:flex;"><span><span style="color:#f92672">- max_monsters_per_room: int,
</span></span></span><span style="display:flex;"><span><span style="color:#f92672">- max_items_per_room: int,
</span></span></span><span style="display:flex;"><span><span style="color:#f92672"></span> engine: Engine,
</span></span><span style="display:flex;"><span>) -&gt; GameMap:
</span></span><span style="display:flex;"><span> ...
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> ...
</span></span><span style="display:flex;"><span> center_of_last_room = new_room.center
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">- place_entities(new_room, dungeon, max_monsters_per_room, max_items_per_room)
</span></span></span><span style="display:flex;"><span><span style="color:#f92672"></span><span style="color:#a6e22e">+ place_entities(new_room, dungeon, engine.game_world.current_floor)
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e"></span>
</span></span><span style="display:flex;"><span> dungeon.tiles[center_of_last_room] = tile_types.down_stairs
</span></span><span style="display:flex;"><span> dungeon.downstairs_location = center_of_last_room
</span></span></code></pre></div>
</div>
<div class="data-pane" data-pane="original">
<pre><span class="crossed-out-text">def place_entities(</span>
<span class="crossed-out-text">room: RectangularRoom, dungeon: GameMap, maximum_monsters: int, maximum_items: int</span>
<span class="crossed-out-text">) -&gt; None:</span>
<span class="crossed-out-text">number_of_monsters = random.randint(0, maximum_monsters)</span>
<span class="crossed-out-text">number_of_items = random.randint(0, maximum_items)</span>
<span class="new-text">def place_entities(room: RectangularRoom, dungeon: GameMap, floor_number: int,) -&gt; None:
number_of_monsters = random.randint(
0, get_max_value_for_floor(max_monsters_by_floor, floor_number)
)
number_of_items = random.randint(
0, get_max_value_for_floor(max_items_by_floor, floor_number)
)</span>
for i in range(number_of_monsters):
...
...
def generate_dungeon(
max_rooms: int,
room_min_size: int,
room_max_size: int,
map_width: int,
map_height: int,
<span class="crossed-out-text">max_monsters_per_room: int,</span>
<span class="crossed-out-text">max_items_per_room: int,</span>
engine: Engine,
) -&gt; GameMap:
...
...
center_of_last_room = new_room.center
<span class="crossed-out-text">place_entities(new_room, dungeon, max_monsters_per_room, max_items_per_room)</span>
<span class="new-text">place_entities(new_room, dungeon, engine.game_world.current_floor)</span>
dungeon.tiles[center_of_last_room] = tile_types.down_stairs
dungeon.downstairs_location = center_of_last_room</pre>
</div>
</div>
<p>We can also remove <code>max_monsters_per_room</code> and <code>max_items_per_room</code> from <code>GameWorld</code>. Remove these lines from <code>game_map.py</code>:</p>
<div>
<button class="btn btn-primary data-toggle-tab active" data-toggle-tab="diff">
Diff
</button>
<button class="btn btn-secondary data-toggle-tab" data-toggle-tab="original">
Original
</button>
<div class="data-pane active" data-pane="diff">
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-diff" data-lang="diff"><span style="display:flex;"><span>class GameWorld:
</span></span><span style="display:flex;"><span> """
</span></span><span style="display:flex;"><span> Holds the settings for the GameMap, and generates new maps when moving down the stairs.
</span></span><span style="display:flex;"><span> """
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> def __init__(
</span></span><span style="display:flex;"><span> self,
</span></span><span style="display:flex;"><span> *,
</span></span><span style="display:flex;"><span> engine: Engine,
</span></span><span style="display:flex;"><span> map_width: int,
</span></span><span style="display:flex;"><span> map_height: int,
</span></span><span style="display:flex;"><span> max_rooms: int,
</span></span><span style="display:flex;"><span> room_min_size: int,
</span></span><span style="display:flex;"><span> room_max_size: int,
</span></span><span style="display:flex;"><span><span style="color:#f92672">- max_monsters_per_room: int,
</span></span></span><span style="display:flex;"><span><span style="color:#f92672">- max_items_per_room: int,
</span></span></span><span style="display:flex;"><span><span style="color:#f92672"></span> current_floor: int = 0
</span></span><span style="display:flex;"><span> ):
</span></span><span style="display:flex;"><span> self.engine = engine
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> self.map_width = map_width
</span></span><span style="display:flex;"><span> self.map_height = map_height
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> self.max_rooms = max_rooms
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> self.room_min_size = room_min_size
</span></span><span style="display:flex;"><span> self.room_max_size = room_max_size
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">- self.max_monsters_per_room = max_monsters_per_room
</span></span></span><span style="display:flex;"><span><span style="color:#f92672">- self.max_items_per_room = max_items_per_room
</span></span></span><span style="display:flex;"><span><span style="color:#f92672"></span>
</span></span><span style="display:flex;"><span> self.current_floor = current_floor
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> def generate_floor(self) -&gt; None:
</span></span><span style="display:flex;"><span> from procgen import generate_dungeon
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> self.current_floor += 1
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> self.engine.game_map = generate_dungeon(
</span></span><span style="display:flex;"><span> max_rooms=self.max_rooms,
</span></span><span style="display:flex;"><span> room_min_size=self.room_min_size,
</span></span><span style="display:flex;"><span> room_max_size=self.room_max_size,
</span></span><span style="display:flex;"><span> map_width=self.map_width,
</span></span><span style="display:flex;"><span> map_height=self.map_height,
</span></span><span style="display:flex;"><span><span style="color:#f92672">- max_monsters_per_room=self.max_monsters_per_room,
</span></span></span><span style="display:flex;"><span><span style="color:#f92672">- max_items_per_room=self.max_items_per_room,
</span></span></span><span style="display:flex;"><span><span style="color:#f92672"></span> engine=self.engine,
</span></span><span style="display:flex;"><span> )
</span></span></code></pre></div>
</div>
<div class="data-pane" data-pane="original">
<pre>class GameWorld:
"""
Holds the settings for the GameMap, and generates new maps when moving down the stairs.
"""
def __init__(
self,
*,
engine: Engine,
map_width: int,
map_height: int,
max_rooms: int,
room_min_size: int,
room_max_size: int,
<span class="crossed-out-text">max_monsters_per_room: int,</span>
<span class="crossed-out-text">max_items_per_room: int,</span>
current_floor: int = 0
):
self.engine = engine
self.map_width = map_width
self.map_height = map_height
self.max_rooms = max_rooms
self.room_min_size = room_min_size
self.room_max_size = room_max_size
<span class="crossed-out-text">self.max_monsters_per_room = max_monsters_per_room</span>
<span class="crossed-out-text">self.max_items_per_room = max_items_per_room</span>
self.current_floor = current_floor
def generate_floor(self) -&gt; None:
from procgen import generate_dungeon
self.current_floor += 1
self.engine.game_map = generate_dungeon(
max_rooms=self.max_rooms,
room_min_size=self.room_min_size,
room_max_size=self.room_max_size,
map_width=self.map_width,
map_height=self.map_height,
<span class="crossed-out-text">max_monsters_per_room=self.max_monsters_per_room,</span>
<span class="crossed-out-text">max_items_per_room=self.max_items_per_room,</span>
engine=self.engine,
)</pre>
</div>
</div>
<p>Also remove the same variables from <code>setup_game.py</code>:</p>
<div>
<button class="btn btn-primary data-toggle-tab active" data-toggle-tab="diff">
Diff
</button>
<button class="btn btn-secondary data-toggle-tab" data-toggle-tab="original">
Original
</button>
<div class="data-pane active" data-pane="diff">
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-diff" data-lang="diff"><span style="display:flex;"><span>def new_game() -&gt; Engine:
</span></span><span style="display:flex;"><span> """Return a brand new game session as an Engine instance."""
</span></span><span style="display:flex;"><span> map_width = 80
</span></span><span style="display:flex;"><span> map_height = 43
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> room_max_size = 10
</span></span><span style="display:flex;"><span> room_min_size = 6
</span></span><span style="display:flex;"><span> max_rooms = 30
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">- max_monsters_per_room = 2
</span></span></span><span style="display:flex;"><span><span style="color:#f92672">- max_items_per_room = 2
</span></span></span><span style="display:flex;"><span><span style="color:#f92672"></span>
</span></span><span style="display:flex;"><span> player = copy.deepcopy(entity_factories.player)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> engine = Engine(player=player)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> engine.game_world = GameWorld(
</span></span><span style="display:flex;"><span> engine=engine,
</span></span><span style="display:flex;"><span> max_rooms=max_rooms,
</span></span><span style="display:flex;"><span> room_min_size=room_min_size,
</span></span><span style="display:flex;"><span> room_max_size=room_max_size,
</span></span><span style="display:flex;"><span> map_width=map_width,
</span></span><span style="display:flex;"><span> map_height=map_height,
</span></span><span style="display:flex;"><span><span style="color:#f92672">- max_monsters_per_room=max_monsters_per_room,
</span></span></span><span style="display:flex;"><span><span style="color:#f92672">- max_items_per_room=max_items_per_room,
</span></span></span><span style="display:flex;"><span><span style="color:#f92672"></span> )
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> engine.game_world.generate_floor()
</span></span><span style="display:flex;"><span> engine.update_fov()
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> engine.message_log.add_message(
</span></span><span style="display:flex;"><span> "Hello and welcome, adventurer, to yet another dungeon!", color.welcome_text
</span></span><span style="display:flex;"><span> )
</span></span><span style="display:flex;"><span> return engine
</span></span></code></pre></div>
</div>
<div class="data-pane" data-pane="original">
<pre>def new_game() -&gt; Engine:
"""Return a brand new game session as an Engine instance."""
map_width = 80
map_height = 43
room_max_size = 10
room_min_size = 6
max_rooms = 30
<span class="crossed-out-text">max_monsters_per_room = 2</span>
<span class="crossed-out-text">max_items_per_room = 2</span>
player = copy.deepcopy(entity_factories.player)
engine = Engine(player=player)
engine.game_world = GameWorld(
engine=engine,
max_rooms=max_rooms,
room_min_size=room_min_size,
room_max_size=room_max_size,
map_width=map_width,
map_height=map_height,
<span class="crossed-out-text">max_monsters_per_room=max_monsters_per_room,</span>
<span class="crossed-out-text">max_items_per_room=max_items_per_room,</span>
)
engine.game_world.generate_floor()
engine.update_fov()
engine.message_log.add_message(
"Hello and welcome, adventurer, to yet another dungeon!", color.welcome_text
)
return engine</pre>
</div>
</div>
<p>Now were adjusting the number of items and monsters based on the
floor. The next step is to control which entities appear on which floor,
instead of allowing any entity to appear on any floor. The first floor
will only have health potions and orcs, and well gradually add
different items and enemies as the player goes deeper into the dungeon.</p>
<p>We need a function that allows us to get these entities at random,
based on a set of weights. We also need to define the weights
themselves.</p>
<p>What are “weights” in this context? Basically, we could define all of
the odds of generating a type of entity the way we have already, by
getting a random number and comparing against a set of values, but that
will quickly become cumbersome as we add more entities. Imagine wanting
to add a new enemy type, but needing to adjust the values for dozens, or
perhaps <strong>hundreds</strong>, of other entities.</p>
<p>Instead, well just give each entity a value, or a “weight”, which
well use to determine how common that entity should be. Well use
Pythons <code>random.choices</code> function, which allows the user to
pass a list of items and a set of weights. It returns a number of items
that you specify, based on the weights you give it.</p>
<p>First, we need to define our weights for the entity types, along with
the minimum floor that the item or monster will appear on. Add the
following to <code>procgen.py</code>:</p>
<div>
<button class="btn btn-primary data-toggle-tab active" data-toggle-tab="diff">
Diff
</button>
<button class="btn btn-secondary data-toggle-tab" data-toggle-tab="original">
Original
</button>
<div class="data-pane active" data-pane="diff">
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-diff" data-lang="diff"><span style="display:flex;"><span>from __future__ import annotations
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>import random
</span></span><span style="display:flex;"><span><span style="color:#f92672">-from typing import Iterator, List, Tuple, TYPE_CHECKING
</span></span></span><span style="display:flex;"><span><span style="color:#f92672"></span><span style="color:#a6e22e">+from typing import Dict, Iterator, List, Tuple, TYPE_CHECKING
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e"></span>
</span></span><span style="display:flex;"><span>import tcod
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>import entity_factories
</span></span><span style="display:flex;"><span>from game_map import GameMap
</span></span><span style="display:flex;"><span>import tile_types
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>if TYPE_CHECKING:
</span></span><span style="display:flex;"><span> from engine import Engine
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">+ from entity import Entity
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e"></span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>max_items_by_floor = [
</span></span><span style="display:flex;"><span> (1, 1),
</span></span><span style="display:flex;"><span> (4, 2),
</span></span><span style="display:flex;"><span>]
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>max_monsters_by_floor = [
</span></span><span style="display:flex;"><span> (1, 2),
</span></span><span style="display:flex;"><span> (4, 3),
</span></span><span style="display:flex;"><span> (6, 5),
</span></span><span style="display:flex;"><span>]
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">+item_chances: Dict[int, List[Tuple[Entity, int]]] = {
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e">+ 0: [(entity_factories.health_potion, 35)],
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e">+ 2: [(entity_factories.confusion_scroll, 10)],
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e">+ 4: [(entity_factories.lightning_scroll, 25)],
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e">+ 6: [(entity_factories.fireball_scroll, 25)],
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e">+}
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e"></span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">+enemy_chances: Dict[int, List[Tuple[Entity, int]]] = {
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e">+ 0: [(entity_factories.orc, 80)],
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e">+ 3: [(entity_factories.troll, 15)],
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e">+ 5: [(entity_factories.troll, 30)],
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e">+ 7: [(entity_factories.troll, 60)],
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e">+}
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e"></span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>def get_max_value_for_floor(
</span></span><span style="display:flex;"><span> ...
</span></span></code></pre></div>
</div>
<div class="data-pane" data-pane="original">
<pre>from __future__ import annotations
import random
<span class="crossed-out-text">from typing import Iterator, List, Tuple, TYPE_CHECKING</span>
<span class="new-text">from typing import Dict, Iterator, List, Tuple, TYPE_CHECKING</span>
import tcod
import entity_factories
from game_map import GameMap
import tile_types
if TYPE_CHECKING:
from engine import Engine
<span class="new-text">from entity import Entity</span>
max_items_by_floor = [
(1, 1),
(4, 2),
]
max_monsters_by_floor = [
(1, 2),
(4, 3),
(6, 5),
]
<span class="new-text">item_chances: Dict[int, List[Tuple[Entity, int]]] = {
0: [(entity_factories.health_potion, 35)],
2: [(entity_factories.confusion_scroll, 10)],
4: [(entity_factories.lightning_scroll, 25)],
6: [(entity_factories.fireball_scroll, 25)],
}
enemy_chances: Dict[int, List[Tuple[Entity, int]]] = {
0: [(entity_factories.orc, 80)],
3: [(entity_factories.troll, 15)],
5: [(entity_factories.troll, 30)],
7: [(entity_factories.troll, 60)],
}</span>
def get_max_value_for_floor(
...</pre>
</div>
</div>
<p>They keys in the dictionary represent the floor number, and the value
is a list of tuples. The tuples contain an entity and the weights at
which theyll be generated. Notice that Trolls get defined multiple
times in <code>enemy_chances</code>, and their weights grow higher when
the floor number increases. This will allow Trolls to be generated more
frequently as the player dives into the dungeon, thus making the dungeon
more dangerous with each passing floor.</p>
<p>Why a <em>list</em> of tuples, though? While there isnt any examples
here, we want it to be possible to define many entity types and weights
for each floor. For example, imagine we added a new enemy type that
appears on floor 5. We could put that as a tuple inside the list,
alongside the Trolls tuple. Well see an example of this in the next
chapter, when we start adding equipment.</p>
<p>With our weights defined, we need a function to actually pick which entities we want to create. As mentioned, it will utilize <code>random.choices</code> from the Python standard library to choose the entities. Add this function to <code>procgen.py</code>:</p>
<div>
<button class="btn btn-primary data-toggle-tab active" data-toggle-tab="diff">
Diff
</button>
<button class="btn btn-secondary data-toggle-tab" data-toggle-tab="original">
Original
</button>
<div class="data-pane active" data-pane="diff">
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-diff" data-lang="diff"><span style="display:flex;"><span>def get_max_value_for_floor(
</span></span><span style="display:flex;"><span> weighted_chances_by_floor: List[Tuple[int, int]], floor: int
</span></span><span style="display:flex;"><span>) -&gt; int:
</span></span><span style="display:flex;"><span> ...
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">+def get_entities_at_random(
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e">+ weighted_chances_by_floor: Dict[int, List[Tuple[Entity, int]]],
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e">+ number_of_entities: int,
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e">+ floor: int,
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e">+) -&gt; List[Entity]:
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e">+ entity_weighted_chances = {}
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e"></span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">+ for key, values in weighted_chances_by_floor.items():
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e">+ if key &gt; floor:
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e">+ break
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e">+ else:
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e">+ for value in values:
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e">+ entity = value[0]
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e">+ weighted_chance = value[1]
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e"></span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">+ entity_weighted_chances[entity] = weighted_chance
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e"></span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">+ entities = list(entity_weighted_chances.keys())
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e">+ entity_weighted_chance_values = list(entity_weighted_chances.values())
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e"></span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">+ chosen_entities = random.choices(
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e">+ entities, weights=entity_weighted_chance_values, k=number_of_entities
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e">+ )
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e"></span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">+ return chosen_entities
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e"></span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>class RectangularRoom:
</span></span><span style="display:flex;"><span> ...
</span></span></code></pre></div>
</div>
<div class="data-pane" data-pane="original">
<pre>def get_max_value_for_floor(
weighted_chances_by_floor: List[Tuple[int, int]], floor: int
) -&gt; int:
...
<span class="new-text">def get_entities_at_random(
weighted_chances_by_floor: Dict[int, List[Tuple[Entity, int]]],
number_of_entities: int,
floor: int,
) -&gt; List[Entity]:
entity_weighted_chances = {}
for key, values in weighted_chances_by_floor.items():
if key &gt; floor:
break
else:
for value in values:
entity = value[0]
weighted_chance = value[1]
entity_weighted_chances[entity] = weighted_chance
entities = list(entity_weighted_chances.keys())
entity_weighted_chance_values = list(entity_weighted_chances.values())
chosen_entities = random.choices(
entities, weights=entity_weighted_chance_values, k=number_of_entities
)
return chosen_entities</span>
class RectangularRoom:
...</pre>
</div>
</div>
<p>This function goes through they keys (floor numbers) and values (list
of weighted entities), stopping when the key is higher than the given
floor number. It sets up a dictionary of the weights for each entity,
based on which floor the player is currently on. So if we were trying to
get the weights for floor 6, <code>entity_weighted_chances</code> would look like this: <code>{ orc: 80, troll: 30 }</code>.</p>
<p>Then, we get both the keys and values in list format, so that they can be passed to <code>random.choices</code> (it accepts choices and weights as lists). <code>k</code> represents the number of items that <code>random.choices</code>
should pick, so we can simply pass the number of entities weve decided
to generate. Finally, we return the list of chosen entities.</p>
<p>Putting this function to use is quite simple. In fact, it will reduce the amount of code in our <code>place_entities</code> function quite nicely:</p>
<div>
<button class="btn btn-primary data-toggle-tab active" data-toggle-tab="diff">
Diff
</button>
<button class="btn btn-secondary data-toggle-tab" data-toggle-tab="original">
Original
</button>
<div class="data-pane active" data-pane="diff">
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-diff" data-lang="diff"><span style="display:flex;"><span>def place_entities(room: RectangularRoom, dungeon: GameMap, floor_number: int,) -&gt; None:
</span></span><span style="display:flex;"><span> number_of_monsters = random.randint(
</span></span><span style="display:flex;"><span> 0, get_weight_for_floor(max_monsters_by_floor, floor_number)
</span></span><span style="display:flex;"><span> )
</span></span><span style="display:flex;"><span> number_of_items = random.randint(
</span></span><span style="display:flex;"><span> 0, get_weight_for_floor(max_items_by_floor, floor_number)
</span></span><span style="display:flex;"><span> )
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">+ monsters: List[Entity] = get_entities_at_random(
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e">+ enemy_chances, number_of_monsters, floor_number
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e">+ )
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e">+ items: List[Entity] = get_entities_at_random(
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e">+ item_chances, number_of_items, floor_number
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e">+ )
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e"></span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">- for i in range(number_of_monsters):
</span></span></span><span style="display:flex;"><span><span style="color:#f92672">- x = random.randint(room.x1 + 1, room.x2 - 1)
</span></span></span><span style="display:flex;"><span><span style="color:#f92672">- y = random.randint(room.y1 + 1, room.y2 - 1)
</span></span></span><span style="display:flex;"><span><span style="color:#f92672"></span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">- if not any(entity.x == x and entity.y == y for entity in dungeon.entities):
</span></span></span><span style="display:flex;"><span><span style="color:#f92672">- if random.random() &lt; 0.8:
</span></span></span><span style="display:flex;"><span><span style="color:#f92672">- entity_factories.orc.spawn(dungeon, x, y)
</span></span></span><span style="display:flex;"><span><span style="color:#f92672">- else:
</span></span></span><span style="display:flex;"><span><span style="color:#f92672">- entity_factories.troll.spawn(dungeon, x, y)
</span></span></span><span style="display:flex;"><span><span style="color:#f92672"></span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">- for i in range(number_of_items):
</span></span></span><span style="display:flex;"><span><span style="color:#f92672"></span><span style="color:#a6e22e">+ for entity in monsters + items:
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e"></span> x = random.randint(room.x1 + 1, room.x2 - 1)
</span></span><span style="display:flex;"><span> y = random.randint(room.y1 + 1, room.y2 - 1)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> if not any(entity.x == x and entity.y == y for entity in dungeon.entities):
</span></span><span style="display:flex;"><span><span style="color:#f92672">- item_chance = random.random()
</span></span></span><span style="display:flex;"><span><span style="color:#f92672"></span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">- if item_chance &lt; 0.7:
</span></span></span><span style="display:flex;"><span><span style="color:#f92672">- entity_factories.health_potion.spawn(dungeon, x, y)
</span></span></span><span style="display:flex;"><span><span style="color:#f92672">- elif item_chance &lt; 0.80:
</span></span></span><span style="display:flex;"><span><span style="color:#f92672">- entity_factories.fireball_scroll.spawn(dungeon, x, y)
</span></span></span><span style="display:flex;"><span><span style="color:#f92672">- elif item_chance &lt; 0.90:
</span></span></span><span style="display:flex;"><span><span style="color:#f92672">- entity_factories.confusion_scroll.spawn(dungeon, x, y)
</span></span></span><span style="display:flex;"><span><span style="color:#f92672">- else:
</span></span></span><span style="display:flex;"><span><span style="color:#f92672">- entity_factories.lightning_scroll.spawn(dungeon, x, y)
</span></span></span><span style="display:flex;"><span><span style="color:#f92672"></span><span style="color:#a6e22e">+ entity.spawn(dungeon, x, y)
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e"></span>
</span></span><span style="display:flex;"><span>...
</span></span></code></pre></div>
</div>
<div class="data-pane" data-pane="original">
<pre>def place_entities(room: RectangularRoom, dungeon: GameMap, floor_number: int,) -&gt; None:
number_of_monsters = random.randint(
0, get_weight_for_floor(max_monsters_by_floor, floor_number)
)
number_of_items = random.randint(
0, get_weight_for_floor(max_items_by_floor, floor_number)
)
<span class="new-text">monsters: List[Entity] = get_entities_at_random(
enemy_chances, number_of_monsters, floor_number
)
items: List[Entity] = get_entities_at_random(
item_chances, number_of_items, floor_number
)</span>
<span class="crossed-out-text">for i in range(number_of_monsters):</span>
<span class="crossed-out-text">x = random.randint(room.x1 + 1, room.x2 - 1)</span>
<span class="crossed-out-text">y = random.randint(room.y1 + 1, room.y2 - 1)</span>
<span class="crossed-out-text">if not any(entity.x == x and entity.y == y for entity in dungeon.entities):</span>
<span class="crossed-out-text">if random.random() &lt; 0.8:</span>
<span class="crossed-out-text">entity_factories.orc.spawn(dungeon, x, y)</span>
<span class="crossed-out-text">else:</span>
<span class="crossed-out-text">entity_factories.troll.spawn(dungeon, x, y)</span>
<span class="crossed-out-text">for i in range(number_of_items):</span>
<span class="new-text">for entity in monsters + items:</span>
x = random.randint(room.x1 + 1, room.x2 - 1)
y = random.randint(room.y1 + 1, room.y2 - 1)
if not any(entity.x == x and entity.y == y for entity in dungeon.entities):
<span class="crossed-out-text">item_chance = random.random()</span>
<span class="crossed-out-text">if item_chance &lt; 0.7:</span>
<span class="crossed-out-text">entity_factories.health_potion.spawn(dungeon, x, y)</span>
<span class="crossed-out-text">elif item_chance &lt; 0.80:</span>
<span class="crossed-out-text">entity_factories.fireball_scroll.spawn(dungeon, x, y)</span>
<span class="crossed-out-text">elif item_chance &lt; 0.90:</span>
<span class="crossed-out-text">entity_factories.confusion_scroll.spawn(dungeon, x, y)</span>
<span class="crossed-out-text">else:</span>
<span class="crossed-out-text">entity_factories.lightning_scroll.spawn(dungeon, x, y)</span>
<span class="new-text">entity.spawn(dungeon, x, y)</span>
...</pre>
</div>
</div>
<p>Now <code>place_entities</code> is just getting the amount of monsters and items to generate, and leaving it up to <code>get_entities_at_random</code> to determine which ones to create.</p>
<p>With those changes, the dungeon will get progressively more
difficult! You may want to tweak certain numbers, like the strength of
the enemies or how much health you recover with potions, to get a more
challenging experience (our game is still not <em>that</em> difficult, if you increase your defense by just 1, Orcs are no longer a threat).</p>
<p>If you want to see the code so far in its entirety, <a href="https://github.com/TStand90/tcod_tutorial_v2/tree/2020/part-12">click here</a>.</p>
<p><a href="https://rogueliketutorials.com/tutorials/tcod/v2/part-13">Click here to move on to the next part of this tutorial.</a></p>
</article>
</section>
</div>
<footer class="footer">
<section class="container">
©
2023
·
Powered by <a href="https://gohugo.io/">Hugo</a> &amp; <a href="https://github.com/luizdepra/hugo-coder/">Coder</a>.
</section>
</footer>
</main>
<script src="Part%2012%20-%20Increasing%20Difficulty%20%C2%B7%20Roguelike%20Tutorials_files/coder.min.236049395dc3682fb2719640872958e12f1f24067bb09c327b2.js" integrity="sha256-I2BJOV3DaC+ycZZAhylY4S8fJAZ7sJwyeyM+YpDH7aw="></script>
<script src="Part%2012%20-%20Increasing%20Difficulty%20%C2%B7%20Roguelike%20Tutorials_files/codetabs.min.cc52451e7f25e50f64c1c893826f606d58410d742c214dce.js" integrity="sha256-zFJFHn8l5Q9kwciTgm9gbVhBDXQsIU3OI/tEfJlh8rA="></script>
</body></html>