Miljøet i vårt tilfelle er en labyrint. Labyrinten skal representeres ved hjelp av en 2D-liste med heltallsverdier. En åpen rute (hvor agenten vår kan gå) representeres av tallet 0
. En veggrute (hvor agenten vår ikke kan gå) representeres av tallet 1
. En målrute (hvor agenten vår ønsker å komme til) representeres av tallet 2
.
For eksempel, 2D-listen [[0, 0, 0], [0, 1, 0], [1, 2, 0]]
representerer denne labyrinten:
Lag en funksjon load_maze_from_file(filename)
i maze.py
som åpner en tekstfil, leser innholdet, og returnerer en 2D-liste som representerer labyrinten. For eksempel, hvis vi har en tekstfil maze.txt
med følgende innhold:
0001
1121
0100
0001
så skal load_maze_from_file("maze.txt")
returnere følgende 2D-liste:
[[0, 0, 0, 1],
[1, 1, 2, 1],
[0, 1, 0, 0],
[0, 0, 0, 1]]
Til slutt skal vi utvide funksjonen vår til å legge til en kant med veggruter (1ere) rundt labyrinten. Med andre ord, legg til 1ere på begynnelsen og slutten av hver rad, og legg til en rad med bare 1ere på toppen og bunnen. Med samme maze.txt
som i eksempelet ovenfor skal nå load_maze_from_file("maze.txt")
nå returnere følgende 2D-liste:
[[1, 1, 1, 1, 1, 1],
[1, 0, 0, 0, 1, 1],
[1, 1, 1, 2, 1, 1],
[1, 0, 1, 0, 0, 1],
[1, 0, 0, 0, 1, 1],
[1, 1, 1, 1, 1, 1]]
Du kan laste ned filen maze.txt
eller lage din egen. Hvis du velger å lage din egen labyrint, sørg for at labyrinten er løsbar fra alle mulige startposisjoner for agenten. Fram til du vet at programmet ditt fungerer som det skal, prøv å ikke lag labyrinten alt for stor (det blir vanskeligere for agenten å lære).
Gå videre til neste deloppgave når du har testet funksjonen din og er overbevist om at den fungerer korrekt.
💡 Hint
- Bruk
with open(filename, "r") as f:
når du skal lese tekstfilen. - Iterer over linjene i filen ved å bruke
readlines()
. For eksempel:for line in f.readlines():
. - Du kan bruke
+
til å sette sammen lister. For eksempel:[1] + [1, 0, 1] + [0]
gir listen[1, 1, 0, 1, 0]
og[[1, 1]] + [[1, 0], [0, 1]] + [[0, 0]]
gir listen[[1, 1], [1, 0], [0, 1], [0, 0]]
.
I filen maze.py
, definer en ny funksjon draw_maze(canvas, maze, x1, y1, x2, y2, agent_pos)
som tegner labyrinten maze
på canvas
.
- Parameteren
canvas
er canvas-objektet (som vi senere importerer frauib_inf100_graphics
) som vi skal tegne på. - Parameteren
maze
er en 2D-liste som representerer labyrinten (formatet somload_maze_from_file
returnerer). - Punktene
(x1, y1)
og(x2, y2)
(alle heltall) er koordinatene til henholdsvis øvre venstre hjørne og nedre høyre hjørne av labyrinten. - Parameteren
agent_pos
er en tupel av heltall(col, row)
som angir agentens posisjon i labyrinten (NB: Her errow
rad-indeks ogcol
kolonne-indeks imaze
og ikke koordinater påcanvas
).
Bruk følgende fyllfarger for de forskjellige rutetypene (og ruten agenten befinner seg i):
- Åpen rute: Hvit.
- Veggrute: Svart.
- Målrute: Blå.
- Agentens rute: Oransje.
📋 Test implementasjonen din: For å teste funksjonen draw_maze
som du har laget, lagre filen test_draw_maze.py
i samme mappe som de andre filene. Når du kjører test_draw_maze.py
skal du få opp et vindu som ser slik ut:
Hvis du får opp et vindu helt likt som det i bildet ovenfor kan du gå videre.
💡 Hint
- Regn ut hvor mange rader og kolonner vi har og bruk dette til å regne ut høyden og bredden til hver rute i labyrinten.
- Bruk en nøstet for-løkke når du tegner rutene.
- For å tegne en rute på
canvas
, bruk funksjonencanvas.create_rectangle(left, top, right, bottom, fill)
frauib_inf100_graphics
. - Bruk verdien av
maze[i][j]
for å bestemme fargen ruten skal ha.
Lag en funksjon get_random_position(maze)
i filen maze.py
som returnerer en tupel (col, row)
med posisjonen til en tilfeldig åpen rute i labyrinten gitt som parametren maze
. NB: col
er kolonne-indeks og row
er rad-indeks slik at rutetypen i posisjon (col, row)
bestemmes av verdien maze[row][col]
.
For eksempel, hvis maze
er 2D-listen
[[1, 0, 2],
[0, 1, 0],
[1, 0, 0]]
så skal get_random_position(maze)
returnere én av følgende tupler: (1, 0)
, (0, 1)
, (2, 1)
, (1, 2)
eller (2, 2)
.
💡 Hint
- Importer
random
ved å legge tilimport random
i begynnelsen avmaze.py
. - Bruk
random.randrange(a, b)
for å få et tilfeldig heltall melloma
ogb - 1
. - Bruk en while-løkke og returner når du finner en åpen rute.
I filen maze.py
, lag to funksjoner is_wall(maze, pos)
og is_goal(maze, pos)
som henholdsvis sjekker om ruten i posisjon pos
er en veggrute eller målrute. Parameteren maze
er labyrinten gitt som en 2D-liste og parameteren pos
er en tupel av heltall (col, row)
som angir ruten vi vil sjekke. Funksjonen is_wall(maze, pos)
skal returnere True
hvis ruten i posisjon pos
er en veggrute (har verdi 1
), og False
ellers. Funksjonen is_goal(maze, pos)
skal returnere True
hvis ruten i posisjon pos
er en målrute (har verdi 2
), og False
ellers.
💡 Hint
- Pakk ut tupelen
pos
somcol, row = pos
og sjekk verdien avmaze[col][row]
.
📋 Test implementasjonen din: Lagre filen test_is_wall_and_is_goal.py
i samme mappe som de andre filene og kjør den for å teste koden din. Hvis du får All test passed!
kan du gå videre.
Lag en funksjon coord_in_direction(pos, direction)
i filen maze.py
. Funksjonen tar in en tupel pos
av heltall på formen (col, row)
og en streng direction
som tar en av følgende verdier: "left"
, "right"
, "up"
eller "down"
. Funksjonen skal returnere en posisjonen til ruten i retning direction
.
For eksempel, coord_in_direction((1, 1), "up")
skal returnere (1, 0)
og coord_in_direction((1, 1), "left")
skal returnere (0, 1)
.
Du skal ikke sjekke om den nye posisjonen er en veggrute eller utenfor brettet.
💡 Hint
- Pakk ut tupelen
pos
somcol, row = pos
. - Koordinatene for ruten til venstre blir
(col - 1, row)
. - Koordinatene for ruten til høyre blir
(col + 1, row)
. - Koordinatene for ruten over blir
(col, row - 1)
. - Koordinatene for ruten under blir
(col, row + 1)
.
📋 Test implementasjonen din: Lagre filen test_coord_in_direction.py
i samme mappe som de andre filene og kjør den for å teste koden din. Hvis du får All test passed!
kan du gå videre.
Lag en funksjon move_agent(agent_pos, direction, maze)
i filen maze.py
som returnerer agentens nye posisjon dersom agenten er i agent_pos
og beveger seg i retning direction
. Parameteren agent_pos
er igjen en tupel med heltall på formen (col, row)
. Parameteren direction
er en streng som har en av følgende verdier: "left"
, "right"
, "up"
eller "down"
. Parameteren maze
er en 2D-liste som representerer labyrinten. Hvis agenten prøver å gå inn i en vegg, returner agent_pos
. Ellers, returner koordinatene til den nye posisjonen.
💡 Hint
- Bruk
coord_in_direction
for å finne koordinatene til ruten i retningdirection
fraagent_pos
. - Bruk
is_wall
for å sjekke om naboruten er en vegg. - Du kan bruke "early return" for å gjøre koden din lettere å lese. For eksempel funksjonen
foo(bar)
nedenfor returnerer 1 hvis bar har verdien "hello", 2 hvis bar har verdien "world" og 0 ellers.
def foo(bar):
if bar == "hello":
return 1
if bar == "world":
return 2
return 0
📋 Test implementasjonen din: Lagre filen test_move_agent.py
i samme mappe som de andre filene og kjør den for å teste koden din. Hvis du får All test passed!
kan du gå videre.
Kopier inn koden under i filen main.py
:
from uib_inf100_graphics.event_app import run_app
from maze import *
from learning import *
WIDTH = 800
HEIGHT = 800
def app_started(app):
... # Din kode her
def timer_fired(app):
... # Din kode her
def key_pressed(app, event):
... # Din kode her
def redraw_all(app, canvas):
... # Din kode her
run_app(width=WIDTH, height=HEIGHT, title="Q-Learning Maze")
1.g.1) Opprett variabler
I funksjonen app_started
, opprett følgende 6 variabler:
app.maze
med verdi lik returverdien tilload_maze_from_file("maze.txt")
,app.agent_pos
med verdi fraget_random_position
,app.timer_delay
med verdi1
,app.is_paused
med verdiFalse
.app.upper_left
med verdi(20, 50)
ogapp.bottom_right
med verdi(WIDTH - 20, HEIGHT - 20)
.
1.g.2) Tastetrykk
I funksjonen key_pressed
implementer følgende funksjonalitet basert på verdien av event.key
:
- Hvis
event.key
er"Left"
,"Right"
,"Up"
eller"Down"
så flytt agenten i tilsvarende retning ved hjelp av funksjonenmove_agent
. - Hvis
event.key
er"Space"
så endre variabelenapp.is_paused
tilTrue
dersomapp.is_paused = False
og vice versa. - Hvis
event.key
er"r"
så sett agenten til en ny tilfeldig posisjon ved å oppdatereapp.agent_pos
medget_random_position
. - Hvis
event.key
er"f"
ogapp.timer_delay > 1
så mink verdien avapp.timer_delay
med1
. - Hvis
event.key
er"s"
ogapp.timer_delay < 500
så øk verdien avapp.timer_delay
med1
.
1.g.3) Pause
I funksjonen timer_fired
returner dersom app.is_paused
er True
. I del 2 skal vi skrive mer kode i timer_fired
som skal kjøre dersom app.is_paused
er False
.
1.g.4) Tegn nåværende labyrint
I funksjonen redraw_all
, tegn labyrinten ved å bruke funksjonen draw_maze
. Her får du bruk for variablene app.maze
, app.upper_left
, app.bottom_right
og app.agant_pos
.
Når du kjører main.py
skal du nå få opp et vindu med labyrinten (definert i maze.txt
) og du skal kunne flytte agenten rundt med piltastene.
Gratulerer, du er nå ferdig med del 1. Trykk her for å komme til del 2.