Wumpus World SLI 370 Eksamen Hsten 1999 av Gyrd Brndeland og Linn Iren Humlekjr. Universitetet i Oslo Institutt for lingvistiske fag 1999-11-29 Innhold 1 En logisk agent 1.1 Konseptet \Wumpus World" . . . . . . . . . . . . . 1.1.1 Spillet |en beskrivelse . . . . . . . . . . . . 1.1.2 Agenten . . . . . . . . . . . . . . . . . . . . 1.1.3 Omgivelsen . . . . . . . . . . . . . . . . . . 1.1.4 Kunnskapsbasen . . . . . . . . . . . . . . . 1.2 Strategier . . . . . . . . . . . . . . . . . . . . . . . 1.2.1 Skestrategi . . . . . . . . . . . . . . . . . . 1.2.2 Prioritering mellom ulike m al . . . . . . . . 1.3 Spr aket, slutningsmekanismen og Prolog . . . . . . 1.3.1 Begrensninger i utsagnslogikk . . . . . . . . 1.3.2 Begrensninger i predikatlogikk . . . . . . . . 1.3.3 Skille mellom fakta og deres representasjon . 1.3.4 Forskjeller mellom Prolog og predikatlogikk 1.3.5 Problemer med eksistens . . . . . . . . . . . 1.3.6 Problemer med negasjon . . . . . . . . . . . 1.3.7 Eektivisering av sk . . . . . . . . . . . . . 1.4 Aktuelle utvidelser av applikasjonen . . . . . . . . . 1.4.1 Omgivelsen . . . . . . . . . . . . . . . . . . 1.4.2 Agenten . . . . . . . . . . . . . . . . . . . . 1.4.3 Kunnskapsbasen . . . . . . . . . . . . . . . 2 Teknisk dokumentasjon 2.1 2.2 2.3 2.4 2.5 2.6 2.7 2.8 2.9 Getting started . . . . . . . . . . . . . . . . . . . Defsystem . . . . . . . . . . . . . . . . . . . . . . Agenten . . . . . . . . . . . . . . . . . . . . . . . Omgivelsen . . . . . . . . . . . . . . . . . . . . . Kunnskapsbasen . . . . . . . . . . . . . . . . . . . Typer og metoder . . . . . . . . . . . . . . . . . . Kommunikasjon mellom agenten, KB og brukeren Miscellaneous . . . . . . . . . . . . . . . . . . . . Eektivisering og optimering . . . . . . . . . . . . 2.9.1 Eektiviserings-tiltak . . . . . . . . . . . . 2.9.2 Optimerings-tiltak . . . . . . . . . . . . . 3 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7 7 7 7 8 9 10 10 10 11 12 12 13 13 13 14 15 16 16 16 17 19 19 20 20 21 21 22 22 22 23 23 24 A Wumpus World |kildekode A.1 wumpus-types.cl A.2 kb-agent.lisp . . . A.3 system.cl . . . . . A.4 optimizing.cl . . . A.5 actions.cl . . . . . A.6 action-value.cl . . A.7 wumpus-rules.cl . A.8 new-values.cl . . A.9 chicke-value.cl . . A.10 abstractions.cl . . A.11 utilities.cl . . . . A.12 init.cl . . . . . . . A.13 pretty-printing.cl A.14 gold-digger.cl . . A.15 prolog-till.cl . . . A.16 onprolog3.lisp . . B Kjringseksempel . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27 27 28 29 30 31 34 38 44 45 48 50 52 53 56 57 60 67 Innledning Jeg gir stort sett faen i rom, men jeg har problemer med tid. |Erlend Loe Dette dokumentet inneholder dokumentasjon for spillet Wumpus World, og er en del av SLI 370 eksamen, hsten 1999, ved Universitetet i Oslo. Dokumentasjonen er delt i to: kapittel 1 inneholder en beskrivelese av spillet og strategien, samt begrunnelse av ulike valg vi har foretatt, kapittel 2 er en teknisk rapport av spillet. Valg av oppgave M alet med oppgaven er a lage en agent som ved hjelp av logisk resonnering kan lse oppgaver i en forholdsvis enkel kunstig omgivelse. Vi har valgt a bruke Wumpus-verden, omtalt i del III, kapittel 6 - 10, i Russel og Norvig (1995). Vi valgte Wumpus-verden, blant annet for a ha en omgivelse hvor det ville vre gjennomfrbart a implementere en logisk slutningsmekanisme som kunne fungere, i lpet av overskuelig tid. nske om en enkel omgivelse I startfasen samarbeidet vi med Karl Otto Dviken Ekle om Robo Rally. Dette spillet er mer omfattende en Wumpus-verden, b ade med hensyn p a selve implementasjonen av brettet og kompleksiteten av omgivelsen. Blant annet kan omgivelsen til Robo rally-spillet sies a vre ikke-deterministisk, fordi nye tilstander i verden ikke bare er bestemt av foreg aende tilstand pluss agentens handling, men ogs a av endringer i verden og andre agenters trekk. Vi skiftet derfor til et enklere spill, hvor implementasjonen av brettet og metodene rundt det er forholdsvis enkel, slik at vi kunne vie mer av arbeidet til a bygge opp en kunnskapsbase for en agent og f a en slutningsmekanisme til a fungere. Vurdering av applikasjonen Vi har oppn add m alsetningen om a lage en agent som bruker et logisk spr ak til a resonnere for a lse oppgaver i Wumpus-verden. Vi har laget et minimum av setninger i Prolog, som er tilstrekkelig for a f a agenten til a fungere. B ade oppgaven som agenten skal lse og omgivelsen er svrt enkel. En kunne tenke seg utvidelser for a gi agenten ere oppgaver, og endringer i omgivelsen som ville gjre den mer kompleks, men dette ville krevd en mer omfattende kunnskapsbase. Kunnskapen agenten har er nok til a lse oppgaven i noen instansieringer av Wumpusverden, men ikke i alle. Den strste utfordringen har ligget i a bygge opp en kunnskapsbase 5 med setninger om verden som er generelle nok til a kunne gjelde i alle tilfeller. Et sprsm al i arbeidet med a lage en logisk agent, er hvor mange fakta om en omgivelse det er mulig a fange inn med logiske setninger. Dette har vist seg vanskeligere enn vi trodde p a forh and. Selv for en s a enkel omgivelse som Wumpus-verden, en det problematisk a gi agenten kategoriske imperativ om allmene gode eller d arlige handlinger. Dersom en skulle prve a fange inn alle mulige situasjoner en kunne tenke seg (hvis det i det hele tatt er mulig), ville ske jobben blitt veldig stor. En mer komplett skestrateg, kan derfor g a p a bekostning av tidskompleksitet og romkompleksitet ved skingen. Det at vi mter denne typen problemer i en s a enkel omgivelse som Wumpus-verden, indikerer at en mer kompleks omgivelse hvor ikke alle fakta er kjent, trolig vil kreve en noe mer eksibel h andtering av hva som er rett og galt. For mer om dette, se 1.4.2. Problemer under utvikling Underveis i arbeidet sttte vi pa problemer med hensyn p a hvilket logisk spr ak vi skulle velge til a representere kunnskap, og sprsm al om hvor detaljert vi klarer a beskrive verden ved hjelp av logikk, se 1.3. Videre hadde vi en del tekniske problemer med optimering av koden, for a f a den til a kjre i Allegro CL 2. Hvordan behandle ting som kunne vrt gjort annerledes Vi har gjort ere valg b ade med hensyn til den fysiske implementasjonen av spillet, og med hensyn p a valg av spr ak for a representere kunnskap. Valgene har ofte vrt styrt av praktiske hensyn, det vi si vi har valgt de metodene vi har f att til a fungere. Men vi mener de valgene vi har foretatt kan forsvares i forhold til beskrivelsen av oppgaven. Der vi har foretatt valg har vi forskt a begrunne dette i dokumentasjonen. Kapittel 1 En logisk agent 1.1 Konseptet \Wumpus World" 1.1.1 Spillet |en beskrivelse Spillet best ar av en kunnskapsbasert agent og en kunstig omgivelse. Det er ingen fysiske spillere, kun en virtuell (agenten) og en statisk spiller (Wumpus). Wumpus World er et lukket informasjons-spill der agenten ikke har informasjon om spill-brettet (omgivelsen). Agenten har et hovedmaal og ere delm al, som hun forsker a oppn a ved et uinformert sk, det vil si agenten kan skille m al-tilstand fra ikke-m altilstand, men har ikke informasjon om hvor mange steg som m a til eller hva kostnaden er, for a komme i m al. Dette gir forvrig et s akalt horisont-problem, som innebrer at agenten ikke kan forutsi eller kaste blikk over spill-brettet for a planlegge neste trekk. Trekkene eller de handlingene som agenten utfrer bestemmes ikke fr etter at agenten har sanset, og informasjonen om de sansede persepsjonene er lagt til kunnskapsbasen (agentens hukommelse). Det er to farer som truer agentens sikkerhet. Agenten kan falle i endelse brnner, og hun kan stte p a en Wumpus. I begge tilfellene vil agenten d p a stedet. Hvert brett genereres tilfeldig og pnget er at agenten skal kunne klare oppgaven sin p a en tilfredsstillende m ate innenfor et sett av ulike brett. For a f a til dette m a reglene i kunnskapsbasen vre b ade generelle og kompakte. Vi nsker a beskrive trekk ved omgivelsen som gjelder i alle mulige permutasjoner av Wumpus-verden med enkle og konsise regler. En viktig utfordring ved a lage en "god" spiller i Wumpus-verden ligger derfor i oppbyggingen av kunnskapsbasen De beste trekkene som agenten foretar er bestemt ut fra strategien og vurdert etter verdien p a konsekvensen av handlingen. 1.1.2 Agenten En m albasert agent har de samme egenskapene som en en modell-basert agent i tillegg til at den har viten om m al og den har evner til a konstruere en indre representasjon av verdenen eller omgivelsen den benner seg i. Denne indre representasjonen bruker agenten til a handle. Den setter seg m al og forsker a oppn a disse. Agenten i Wumpus World er en m al-basert 7 agent. Hun har en indre representasjonen av persepsjoner, som hun bruker til a utfre og valuere handlinger. P a grun av dette kan vi si at hun til en viss utstrekning er autonom (jfr AI s 49). Til tross for at hun famler rundt i mrke, kan hun via sine sanser, vite om det det er gull i det rommet hun er eller om det er en Wumpus eller et sort hull i umiddelbar nrhet. Hun resonnerer seg frem, via kunnskapsbasen, til hva som til enhver tid er delm al og hva det overordnede (hoved-) m alet er. Hovedm alet er a nne gullet for s a a komme seg til tilbake til *start*, levende og p a kortest mulig antall skritt. Siden agenten vet om hun brer med seg gullet, er hun i stand til a utfre fruktbare handlinger i forhold til de delm alene som er satt n ar agenten benner seg i en gitt situasjon. Agentens sentrale bestanddeler er en kunnskapsbase og en slutningsmekanisme. Kunnskapsbasen inneholder setninger, som representerer fakta om verden. Agenten har et logisk spr ak som den bruker til a representere fakta om verden, og en fysikalistisk del av sensorer som mottar input fra omgivelsen. Etterhvert som agenten g ar rundt p a brettet, oppdaterer den kunnskapsbasen med de erfaringene den gjr seg. Ved hjelp av regler og erfaringer skal agenten kunne slutte seg til hvor det er trygt/ikke trygt a g a, hvor det er lurt a g a osv. 1.1.3 Omgivelsen Kort beskrevet best ar omgivelsen til Wumpus-verden av en hule, representert som et brett med 4x4 ruter, omgitt av vegger. Et monster og en klump med gull plasseres tilfeldig p a brettet, men aldri i startposisjonen. I tillegg kan hver rute med sannsynlighet 0.2 vre en bunnls brnn. Agenten starter i rute (1 1), i henhold til det kartesiske koordinatsystemet (se ogs a avsnittet om koordinater, seksjon 2.8). En mer utfrlig beskrivelse av omgivelsen for Wumpus-spillet gis p a side 154 - 155 i "Articial Intelligence". P a side 46 i "Ariticial Intelligence" gis en oversikt over noen ulike omgivelser og deres karakteristikker. Vi gir her en kort oppsummering av de viktigste distinksjonene: * Aksessibelt versus inaksessibelt, som g ar p a hvorvidt den komplette tilstanden til en omgivelse er tilgjengelig fra agentens sesorer eller ikke. * Determisitisk versus ikke-deterministisk. Det vil si hvorvidt neste tilstand av omgivelsen er bestemt kun av foreg aende tilstand samt agentens handling, eller ikke. * Episodisk versus ikke-episodisk. I en episodisk omgivelse er agentens erfaringshistorie delt opp i "episoder" som best ar av sensor-input for agenten + en handling. Kvaliteten av handlingene avhenger kun av episoden selv, og agenten trenger dermed ikke tenke framover. * Statisk versus dynamisk. En omgivelse sies a vre dynamisk hvis den kan endre seg samtidig med at agenten resonnerer, ellers er den statisk. En kan ogs a ha semidynamiske omgivelser hvor omgivelsen ikke endrer seg, men kvaliteten p a responsen endres. * Diskret versus kontinuerlig. En diskret omgivelse kjennetegnes ved at det nnes et endelig antall utvetydige beskrivelser av mulige handlinger og sensor-input. En omgivelse er kontinuerlig n ar uforutsatte, ikke-denerte fenomener plutselig oppst ar. Hvilken type omgivelse man velger har betydning for utforming av agenten. Det nnes ere typer av omgivelser og hvor kompleks den er avhenger av dens karakteristika. V ar versjon av Wumpus-verden er en av de enklere omgivelsene man kan lage. Den er riktignok ikke aksessibel; agenten har kun oversikt over den ruten hun benner seg i. Av andre mulige karakteristikker passer imidlertid Wumpus-verden med de enklere variantene. Omgivelsen er deterministisk - det er kun agenten som ytter p a seg. Derfor vil de trekkene ved verden som agenten oppdager holde seg stabile igjennom et helt spill. V ar Wumpus-verden er ogs a episodisk, statisk og diskret. En kan argumentere for at det ikke nnes et endelig antall regler om trekk ved verden. Det vil sannsynligvis alltid vre mulig a utvide kunnskapsbasen med regler om sammenhenger i verden og nytten av ulike handlinger for a gjre agenten enda bedre, slik at omgivelsen ikke kan kalles hundre prosent deterministisk. Poenget er imidlertid at det kun nnes et endelig antall lovlige handlinger (g a fram, snu til venstre, snu til hyre, skyt, grip og klatr) og mulige sensor-input (lukt, vind, glitring, skrik og bump). Slik vi tolker Russel/Norvig er det dette som avgjr om verden er diskret. 1.1.4 Kunnskapsbasen Kunnskapsbasen best ar av * Konkrete ting som gull * Hendelse, ting i tid og rom * Historieforlp * Valuerte handlinger Oppbygging av kunnskapsbasen Kunnskapsbasen er delt i tre. Filene wumpus-rules inneholder setninger om sammenhenger i verden. Filene action-value og new-values inneholder nytteregler, hvor en verdi knyttes til en handling, som agenten bruker p a sin vei til a oppn a m alene. N ar agenten har funnet gull m a hun skifte strategi, og da m a handlingene evalueres p a en annen m ate enn tidligere. Hvordan man skal f a til dette kan gjres p a ere m ater, for eksempel ved a la hver regel sjekke om agenten holder gullet eller ikke. Vi valgte isteden a legge setningene om verdier i to atskilte baser, og bytte base n ar agenten har gullet. P a den m aten begrenser vi sket og tydeliggjr at det dreier seg om ulike m al/delm al. Hvordan representere kunnskap Et sentralt problem i oppbygging av kunnskapsbasen er hvorvidt en klarer a lage setninger som til sammen beskriver alle mulige sammenhenger i verden, og hvordan kunnskapen skal representeres. Dette henger blant annet sammen med hvor utrykksfult spr aket en velger er, og vi redegjr grundigere for problemer rundt dette i 1.3.2. I valg av ontologi har vi i stor grad fulgt oppskriften i AI . En vanskelighet var hvordan vi skulle skille mellom situasjoner, eller hvordan vi skulle representere tid. I eksemplene fra AI flger det med en situasjonsparameter, s, i setningene om verden. Vi tolket denne som en tidsparameter, t, som kes med 1 for hver ny situasjon. En utfordring i oppbygging av kb ligger i a uttrykke kompakte regler, det vil si regler som er generelle nok til a kunne brukes i ere situasjoner. Etter hvert som vi lager nye regler, kan vi da bruke de grunnleggende reglene vi allerede har laget. V ar kunnskapsbase er spesialisert til a h andtere fakta om klassen av mulige Wumpus-verdener. 1.2 Strategier 1.2.1 Skestrategi Vi tar her med re kriterier som vi vil vurdere v art valg av skestrategi etter. * Kompletthet; vil agenten kunne oppn a de m alsetningene den har satt seg for alle brett som genereres? * Tidskompleksitet; vil m alet n as i endelig tid? * Romkompleksitet; vil det vre nok minne slik at agenten kommer i m al? * Optimalitet; nner agenten den beste og letteste vei a komme i m al? I henhod til kriteriene over, kan vi si at v ar skestrategi ikke oppfyller kompletthets-kriteriet, det vil si at agenten ikke er garantert a komme i m al selv om det nnes en mulig vei til gullet. Problemet er at agenten ikke har tilgang p a hele sannheten om omgivelsen. Det vil alltid vre sprsm al hvor det ikke nnes kategoriske svar og agenten m a derfor av og til handle i usikkerhet. For eksempel vil en agent ofte ikke vre i stand til a avgjre hvilken rute av to som er en brnn. Tidskompleksitet er tilfredsstillbar. Agenten bruker litt tid, men er i stand til a n a m alet, eller gi opp hvis den ikke nner en sikker vei, i endelig tid og unng a uendelige looper. N ar det gjelder romkompleksiteten har vi nok minne slik som systemet ser ut n a. Men ved en utvidelse av kunnskapsbasen vil vi kunne f a problemer. V ar algoritme oppfyller ikke kriteriet om optimalt sk, fordi den ikke garanterer den beste og strake veien til m alet. Dette skyldes blant annet at agenten utfrer et s akalt blindt-sk. 1.2.2 Prioritering mellom ulike m al Et m al for agenten er a nne gullet. Et annet m al er a overleve, det vil si bevege seg i trygge omr ader. Et tredje m al g ar p a eektivitet. Vi nsker at agenten ikke skal surre for mye rundt, men nne gullet raskest mulig. Vi har forskt a sikre at agenten flger denne strategien ved a formulere at det er best a g a til trygge uutforskede steder, nest best a g a til trygge utforskete steder og tredje best a g a til utrygge steder. Agenten skal under ingen omstendighet g a til steder den vet er ddelige. Hvor bra agenten lser oppgaven vil avhenge av hvor godt reglene klarer a fange inn det en nsker a oppn a. Vi har valgt et minimum av nytteregler som skal til for at agenten klarer a lse oppgaven i noen av verdenene. Men vi kunne sannsynligvis klart a f a agenten til a lse oppgaven raskere eller klart a h andtere ere verdener ved a utvide reglene, eller kanskje nmasket dem enn a mer. Nytteverdien av en handling er som nevnt bestemt ut fra hvordan vi vurderer konsekvensen av handlingen. Vi har forskt a holde oss til en vurdering av de beste handlingene (great) til a vre m alrettet adferd, de nest beste (good) til a vre trygge handlinger n ar agenten er p a jakt efter gull og m alrettede handlinger n ar gullet er funnet og agenten er p a vei hjem. I dette ligger ogs a en del av arsaken til at en ny l blir lastet inn i hukommelsen. \Medium" handlinger er de aksjonene som er safe, men ikke ndvendigvis i riktig retning. \Risky" handlinger er handlinger hvis konsekvensene er usikre. De handlinger som medfrer dden er de karakterisert som \deadly" handlinger. M alet om a nne gullet trygt vil ikke vre oppn aelig innenfor alle typer verdener, noen ganger kan gullet ligge nede i en brnn. Andre ganger kan det nnes en vei til gullet, men agenten er ndt til a ta sjanser for a nne det. Dette ga problemer n ar vi skulle denere nytten av ulike handlinger. I utgangspunktet skal agenten snu og prve en trygg vei, hvis veien fram er riskabel. Men hva er riktig a gjre n ar alle bomsikre veier er utprvd? Vi har kjrt to agenter med ulik strategi; en \feig" som setter sin egen sikkerhet hyere enn m alet om a nne gullet, og en som begynner a ta sjanser n ar de sikre veiene er utprvd. Den dristige agenten klarer a nne gullet litt oftere enn den \feige", men s a har ogs a den dristige agenten en hyere ddsrate. Ikke overraskende scoret den \feige" agenten gjennomsnittlig hyere enn den "dristige" da straen for a d er mye strre en gevinsten ved a nne gullet. For sjekke ulike strategier opp mot hverandre kan man lage statistikk over hvor bra en agent gjr det med funksjonen make-statistics. Den kalles med antall ganger man vil at agenten skal gjennomfre spillet, og en strategi. Mulige forbedringer av skestrategien Som nevnt vil agentens yteevne avhenge av hvor godt reglene klarer a fange inn det en nsker a oppn a. Vi har ikke gjort noen grundige underskelser av hvilke nytteverdier som vil fungere best, utover hva vi tror er mest hensiktsmessig, og hva vi har sett fungerer i bestemte situasjoner, og s a denere nytteverdiene en gang for alle. Vi vil kunne f a agenten v ar til a lse oppgaven i et strre antall Wumpus-verdener hvis vi jobbet mer med reglene. En m ate kunne vrt a tillatt s akalt stokastisk sk, hvor agenten av og til kunne velge handlinger som ikke var denert som optimale. Etter a ha kjrt ere agenter gjennom en serie spill kunne en sett hva som fungerte best, og g att inn og justert standarden i henhold til hva man hadde funnet ut. Slik reglene er n a, kan agenten noen ganger velge tilfeldig mellom jevngode handlinger, men det er ikke lagt opp til at den skal lre av det. 1.3 Spr aket, slutningsmekanismen og Prolog Et viktig sprsm al i arbeidet med a lage en kunnskapsbasert agent er hvilket spr ak en skal velge, for a representere kunnskapen til agenten. Hvilket logisk spr ak en velger har betydning for hvilken type slutningsmekanisme en nsker a bruke. 1.3.1 Begrensninger i utsagnslogikk Vi laget frst en agent som bruker utsagnslogisk spr ak til a representere kunnskap om verden og regler i utsagnslogikk til a resonnere om mulige handlinger (funksjonen gold-digger i len gold-digger.cl). Til dette brukte vi en slutningsmekanisme i utsagnslogikk hentet fra AI. Ved hjelp av denne klarte agenten a gjre noen f a trekk i Wumpus-verden. Men det viste seg fort at utsagnslogikk ikke er uttrykksfullt nok til a h andtere selv en s a enkel omgivelse. Fordi det utsagsnslogiske spr aket kun best ar av enkle utsagn, vil en kunnskapsbase med regler om mulige posisjoner i alle mulige permutasjoner av Wumpus-verden blir alt for stor til a vre praktisk h andterlig. N ar utsagnslogikken viste seg a ikke strekke til, nsket vi i utgangspunktet a utvide til det predikatlogiske spr aket, som i tillegg til utsagn kan representere ting og relasjoner mellom ting (for mer om representasjon i predikatlogikk, se kapittel 7 i AI). Videre nsket vi a bruke resolusjon til a resonnere om verden (for mer om bevismetoder i predikatlogikk se kapittel 9 i AI). Russel/Norvig har laget en skisse for en teorembeviser ved hjelp av resolusjon tilsvarende OTTER, s. 311 i AI. Men vi kk ikke denne funksjonen til a virke og kom fram til at det ville bli for mye arbeid og skulle lage en teorembeviser fra grunnen av p a egen h and. Vi valgte derfor a bruke Prolog som spr ak og slutningsmekanisme istedenfor regulr predikatlogikk. Prolog tilsvarer ikke helt predikatlogikk, verken i spr aket eller i slutningsmekanismen, men vi fant at Prolog allikevel ville tjene v are form al for en agent i Wumpus-verden. Prologs mekanisme for mnsterpassing og unikasjon passer bra til v art form al, da sking etter hvilke handlinger agenten skal foreta skjer ved mnsterpassing og unikasjon. Agenten sender inn et sprsm al av typen handling(?variabel tid) til kunnskapsbasen og f ar tilbake en handling bundet til ?variabel. N ar vi hadde valgt Prolog som slutningsmekanisme ga ogs a det logiske spr aket seg selv. 1.3.2 Begrensninger i predikatlogikk Et problem med bruke predikatlogikk eller Prolog til beskrive en verden hvor ikke alle fakta er kjent, er at predikatlogikk ikke gir noe rom for tvil. Ting er enten sant eller usant. Dette kan bli for rigid, selv for en s a enkel omgivelse som Wumpus-verden. Noe som er bra i situasjon n, gitt visse kriterier, er ikke ndvendigvis bra i situasjon m gitt de samme kriteriene. Dermed blir det vanskelig a gi generelle regler, som er sanne eller gale i alle situasjoner. For eksempel beskrev vi frst regler som sa: pkt 1: Det er bra a g a fram, hvis agenten ikke har gullet, ruten rett fram er trygg og hun ikke har beskt den fr. Hvis ruten foran er utrygg er det bra a snu. Disse reglene holder i mange situasjoner. Men i noen tilfeller kan reglene f a agentene til a g a fram og tilbake mellom to utrygge ruter. I enkelte av spillene er agenten ndt til a ta sjanser for a klare a nne gullet. Men hvordan skal skille mellom situasjoner n ar det er lurt a snu, og n ar det er p a tide a ta en sjanse? Vi lste dette ved a si at agenten skulle ta en sjanse, eventuelt gi seg hvis ruten den siste beskte var blitt beskt mer enn en gang. Sannsynligvis vil vi kunne nne ere spesielle situasjoner som krever egne regler. Vi kan risikere a f a en veldig stor kunnskapsbase dersom vi skal fange inn alle mulige situasjoner, og da nrmer vi oss problemet ved utsagnslogikk. For en agent i Wumpus-verden er disse problemene til a leve med, men for mer komplekse omgivelser, hvor alle fakta ikke er tilgjengelig for agenten, vil en kanskje trenge en noe mer eksibel h andtering av hva som er rett og galt. 1.3.3 Skille mellom fakta og deres representasjon Vi skiller mellom den fysiske omgivelsen og agentens representasjon av fakta om verden. Representasjon av fakta tar det logiske spr aket og slutningsmekanismen, Prolog, seg av. Metodene og brettet tilhrer den fysiske delen av verden. Funksjonen implement-action i la actions.cl operer i grenseland mellom det logiske og det fysiske spr aket; den f ar en handling fra slutningsmekanismen og oppdaterer den fysiske verden. Funksjonen tell i la prolog-till.cl oppdaterer agentens representasjon av verden. 1.3.4 Forskjeller mellom Prolog og predikatlogikk Programmering i Prolog ligner ganske mye p a predikatlogikk b ade i syntaks og bevismetode. Et prolog-system er basert p a en teorembeviser for Horn-klausuler som bruker prinsippet om resolusjon. Men Prolog gir ikke full predikatlogikk. Blant annet har negasjon en litt annen betydning i Prolog enn i predikatlogikk. Prolog likestiller det at noe ikke kan bevises med at det er usant, fordi det antar at den vet alt som er sant. Videre kan en ikke uttrykke disjunksjon helt p a samme m ate som i predikatlogikk Prolog har ogs a problemer med a representere eksistenskvantorer. Valg av Prolog som slutningsmekanisme legger fringer p a syntaksen, fordi Prolog bare aksepterer Horn-klausuler. Hver setning enten er en atomr setning eller en implikasjon uten negerte antecedencer og med en atomr konsekvent. Klausuler i Prolog skrives dessuten "motsatt vei" av predikatlogikk, med konsekventen p a venstre side av implikasjonspila. I oppbyggingen av reglene har vi i stor grad basert oss p a forslagene i AI, s. 203 - 211. Mange av reglene var noks a greie a oversette, ved a snu p a dem. Men enkelte regler var vanskelig a oversette p a grunn av Prologs problemer med a uttrykke negasjon og eksistens, slik de flgende eksemplene viser. 1.3.5 Problemer med eksistens Siden eksistenskvantorene ikke lar seg oversette direkte, m atte vi i uttrykk med eksistens, nne et tilnrmet uttrykk som best mulig bevarte meningen i det opprinnelige uttrykket. Et eksempel er regelen som skal hjelpe agenten til aW slutte hvor monsteret er V l 1; sS melly (l 1) l 2At(W umpus; l 2; s) (l2 = l1 Adj acent(l1; l2)))) Denne lar seg ikke oversette direkte til prolog, men vi klarer likevel a uttrykke det vi nsker. Vi vil at agenten skal kunne resonnere seg fram til at monsteret er i en bestemt rute, enten fordi hun har beskt naborutene og fant at de luktet vondt, eller fordi hun ut i fra tidligere erfaringer kan slutte at det bare er en mulig rute monsteret kan vre p a. Dette gjorde vi ved a dele opp i tre regler: 8 )9 (<- (not-wumpus ?location) (and (adjacent ?location ?neighbour) (visited ?neighbour) (not (smelly ?neighbour)))) ii (<- (at 'Wumpus ?location) (not (wall ?location)) (and (adjacent ?location ?neighbour) (smelly ?neighbour) (and (locationtoward ?neighbour 0 ?L1) (or (lisp (equal ?L1 ?location)) (wall ?L1) (not-wumpus ?L1))) (and (locationtoward ?neighbour 90 ?L2) (or (lisp (equal ?L2 ?location)) (wall ?L2) (not-wumpus ?L2))) (and (locationtoward ?neighbour 180 ?L3) (or (lisp (equal ?L3 ?location)) (wall ?L3) (not-wumpus ?L3))) (and (locationtoward ?neighbour 270 ?L4) (or (lisp (equal ?L4 ?location)) (wall ?L4) (not-wumpus ?L4)))) (cut)) iii (<- (at 'Wumpus ?location) (not (wall ?location)) (and (and (locationtoward ?location 0 ?l1) (or (smelly ?l1) (wall ?l1))) (and (locationtoward ?location 90 ?l2) (or (smelly ?l2) (wall ?l2))) (and (locationtoward ?location 180 ?l3) (or (smelly ?l3) (wall ?l3))) (and (locationtoward ?location 270 ?l4) (or (smelly ?l4) (wall ?l4))))) Regel ii sier at flgende kriterier m a vre oppfylt for a kunne slutte at Wumpusen er i en bestemt rute: Det nnes en naborute som lukter vondt og alle naborutene til naboruta (naboene til naboen), er enten den samme som ruta vi spr etter, en vegg, eller vi vet at det ikke er en Wumpus der. 1.3.6 Problemer med negasjon Prolog kan ikke representere at noe ikke er tilfelle. Det vil si du kan ikke ha en regel av typen: (<- (not (at (wumpus, l1, t)))) Denne mangelen ga ogs a noe problemer da vi skulle uttrykke reglene om hvor Wumpusen er. Som nevnt nsket vi i regel ii a ha med som kriterium at monsteret ikke skulle vre p a en rute. Men hvis vi skrev noe ala: at(wumpus, l, result(a, s)):- cond1 . . not(at(wumpus, neighnours(neighbour))). kk vi en selvrefererende regel som ble g aende i evig lkke. For a kunne uttykke at monsteret ikke var et bestemt sted laget vi derfor regelen: (<- (not-wumpus ?location) (and (adjacent ?location ?neighbour) (visited ?neighbour) (not (smelly ?neighbour)))) For mer om forholdet mellom Prolog og predikatlogikk, se Clocksin og Mellish (1987) (s. 221 - 242), Bratko (1990) (s. 63, 64), Norvig (1992) (s. 465 - 468), Russel og Norvig (1995) (s. 304 - 308). 1.3.7 Eektivisering av sk For a f a skingen i Prolog til a g a litt fortere har vi gjort noen grep som begrenser skerommet. I noen av reglene som vi vet bare ang ar et bestemt individ som for eksempel denne: (<- (at 'dumpy ?location ?s1) (or (and (is ?s0 (- ?s1 1)) (action 'forward ?s0) (locationahead 'dumpy ?location ?s0) (not (wall ?location))) (and (is ?s0 (- ?s1 1)) (action ?a ?s0) (not (lisp (eq ?a 'forward))) (at 'dumpy ?location ?s0)))) Her har vi valgt a la regelen snakke om konstanten 'dumpy (navnet p a agenten), i stedet for en mer generell variant, som gjelder for alle. Dette er ogs a fordi det er forskjellige regler for hvorvidt 'dumpy er et sted og hvorvidt 'wumpus eller en brnn er et sted. Med denne avgrensingen slipper vi at programmet prver a instansiere alle regler som begynner med at, n ar den skal nne ut om agenten er et sted. Videre har vi valgt a oppdatere lokalisering og orientering til agenten etter hver handling i spill-lkka, med funksjonen tell-rst i la prolog-till.cl. N ar vi ikke gjorde det ville Prolog gjr det samme skene hver eneste gang. N ar den skulle nne ut om en handling var lur i tid n, ville den frst ske seg ned til hvor agenten var i tid 0 og deretter instansiere tilstander for hvert tidspunkt fram til tid n. En annen eektivisering er funksjonen push-rule, som pusher reglene frst i listen slik at prolog ikke trenger a lete seg ned til bunnen for a f a tak i siste regel som er lagt til regelbasen. 1.4 Aktuelle utvidelser av applikasjonen Spillet Wumpus World er s a og si blottet for Ægheter ala det a kunne g a til nye niv a (level) eller ke vanskelighetsgraden og sette nye delm al. Slike ideer kan man f.eks hente fra et hvilket som helst Play Station-spill. Vi nner det irrelevant a utvide spillet p a slike m ater. Derfor inneholder denne seksjonen forslag til vidreutvikling av metoder og teknikker. 1.4.1 Omgivelsen N ar vi har f att slutningsmekanismen p a plass i denne enkle omgivelsen, kan en se for seg mulige utvidelser av omgivelsen, som vil gjre den mer kompleks. En ser ganske fort at utvidelser i omgivelsen ofte vil kreve endringer i agentens kunnskapsbase og slutningsmekansime. For eksempel kan en forholdsvis enkel m ate a gjre omgivelsen ikke-deterministisk p a vre a la monsteret Wumpus, ytte p a seg. Persepsjon av lukt sammen med tidligere erfaringer ville da ikke vre nok til a slutte hvor Wumpusen er, og agenten f ar behov for a kunne si noe om sannsyligheten for at Wumpusen er p a ulike steder. I en ikke-episodisk omgivelse vil agenten m atte ta hensyn til hele sitt handlingsforlp og persepsjonsregister. Den har da mulighet til a planlegge. En ikke-episodisk utvidelse av Wumpus World vil g a p a bekostning av rom-kompleksiteten til skestrategien. Hadde vi tillatt et ukjent antall monstre bevege seg i rommet, ville agenten f att en ikke-statisk verden a forholde seg til. Neste avsnitt omhandler blant annet konsekvenser en ikke-statisk omgivelse ville ha f att for agenten. 1.4.2 Agenten En relevant utvidelse av agent-type ville vre a lage en desisjons-agent. En desisjons-agent er en agent som foretar beregninger av sannsynligheter og benytter utility-teori. Dette innebrer at agenten n a m a oppdatere, kontinuerlig, sannsynligheten for hvordan den tror verden (omgivelsen) er. Med en ikke-statisk omgivelse (som vi f ar n ar f.eks monstret beveger seg rundt i omgivelsen) vil en desisjons-agent kunne planlegge og dermed oppdatere forbindelsen mellom handlinger og sannynligheter for hvordan verden blir. En slik agent vil velge handlinger med maksimal nytte. H andtering av usikkerhet Som nevnt f ar vi ikke til a lage en komplett skestrategi for agenten, fordi den ikke har tilgang til hele sannheten om omgivelsen. Det vil alltid vre sprsm al hvor det ikke nnes kategoriske svar og agenten m a derfor av og til handle i usikkerhet. I de este tilfeller i Wumpus-verden vil sannsynligheten for at noe er tilfelle vre 50/50, s a en trenger ikke en egen teori for a h andtere usikkerhet. Men innen et omr ade som medisin, vil predikatlogikk alene ikke strekke til som slutningsmekansime. Hvis vi skulle utvidet agenten til a h andtere usikkerhet ville det f a konsekvenser for valg av logisk spr ak, og hvordan vi representerer agentens kunnskap. Vi nsker ikke lenger setninger som sier at noe enten er sant eller galt. Sannsynlighetsteori er en metode for a knytte usikkerhet/sannsynlighet til regelbaserte systemer. Innenfor sannsynlighetsteori snakker en om b ade prinsipiell uvitenhet; en kjenner ikke alle fakta om et domene, og usikkerhet som skyldes "latskap"; det er for mye arbeid og liste opp hele mengden av antecedencer og konsekvenser som trengs for a sikre en regel uten unntak. Sannsynlighet gir en m ate a summere usikkerheten som skyldes latskap og uvitenhet, (s. 417 AI). I henhold til sannsynlighetsteori kan en agent regne ut sannsynligheten av noe gitt noe annet, forutsatt at den kjenner til fakta om hvor ofte noe forekommer og hvor ofte noe annet forekommer sammen med noe. Dette kalles betinget sannsynlighet. P(A|B) = p(A ^ B)/p(B) Kommunikasjon med andre agenter Det er ogs a mulig a lage en kommunikasjons-agent som snakker (spr og forteller) med andre agenter via hverandres kunnskapsbaser. 1.4.3 Kunnskapsbasen En kunne tenke seg at kunnskapsbasen ble gjort mer generell til a h andtere liknende verdener, ved en enda nere inndeling av fakta. For eksempel kunne man denere en egen kategori av hindere som agenten skal unng a, hvor Wumpus og brnn begge er objekter av typen hinder. Kapittel 2 Teknisk dokumentasjon 2.1 Getting started Vi hadde problemer med ske-teknikkene som gjorde et Franz Allegro CL til tider ikke klarte a kjre hele spillet helt igjennom. Det viste seg imidlertid at Macintosh CL klarte a h andtere disse problemene, slik at hvis du kjrer systemet p a MCL vil du vre garantert at applikasjonen ikke krsjer. Derfor vil du nedenfor nne tre beskrivelser for a kunne kjre applikasjonen fra plattformermene Unix, Microsoft Windows og Macintosh. (Se seksjon 2.9.2 for nrmere beskrivelse av optimerings-tiltakene vi far foretatt.) Vi anbefaler at systemet testes ut i Windows- og Mac-implementasjonene fordi agenten tross alt er, til tider, i stand til a n a delm al som a nne gullet, og man f ar muligheten til a teste ut defsystem, som er en implementasjons-avhengig feature. P a disketten nner du en mappe for hver av de tre plattformene. Felles for alle applikasjonene er at system-len system.cl inneholder de riktige path'ene (dirigert til disketten), og er den eneste la som trener a load'es manuelt. Kjring via Allegro CL p a Dec Vi starter systemet ved a bruke riktig pakke. Dette kan man gjre med top-level kommadoen :package user Deretter loader du la system.cl. Kaller du n a p a (compile-wumpus) i listener vil systemet kompilere alle lene angitt i defsystem (se seksjon 2.2), og alle de kompilerte lene vil loades. Systemet er n a klar for a dumpes. Dette gjr du med funksjonen (dumplisp :name "dumpy.dxl"). Fra n a av kan du starte lispen med et Wumpus World-image: lisp -I dumpy.dxl. Dermed holder det n a med a kalle p a (wumpus-world) for a starte spillet. (Se seksjon 1.3.7 for nrmere beskrivelse av image.) Kjring via Allegro CL Windows Kjrer du systemet fra ACL for Microsoft Windows, laster du inn len system.cl, kaller funksjonen [ved frste gangs kjring av spillet] (comilewumpus) og deretter holder det med a kalle (wumpus-world). Vr oppmerksom p a at n ar du kompilerer lene for frste gang vil Allegro entre debugger. Du skal da dobbelt-klikke p a den verste linjen som sier \set the function denition of name compiler:tail-call-self-mergeswitch anyway." for a kompilere alle lene. 19 N ar du f ar feilmelingen \ Error: Stack overow (signal 1000) [condition type: synchronousoperating-system-signal]" kan du godt forske a tvinge lispen til vidre kjring, ved a dobbeltklikke p a \continue computation". Vi har funnet at det g ar an a presse lispen til a fortsette sket. I len kjrings-ex.txt i \Wumpus-Windows"-mappen nner du et kjringseksempel der agenten oppn ar hovedm alet. Kjring via Macintosh CL Filene kompileres med compile-wumpus, og spillet startes med wumpus-world i la system.cl, som du nner p a disketten i mappa Wumpu-system. 2.2 Defsystem Allegro Common Lisp har en innebygget feature kalt defsystem. Dette er en feature som gir utviklere av store applikasjoner meget god kontroll p a kompilering og load'ing av ler. Man kan bl.a dirigere ler til a kompileres eller/og loades parallelt eller sekvesielt. I tillegg gir defsystem oss mulighet til a se/analysere hvilke ler som avhenger av andre. Vi har brukt defsystem for a ha kontroll med kompilering og loading av lene vi bruker i spillet. Filene er gruppert og tillagt navn med relevans for den modulene de tilhrer. P a denne m aten unng ar vi problemer som at en funksjon ikke kan kompileres fordi den bruker en annen funksjon som enn a ikke er kompilert. I len \system.cl" nner du defsystem til Wumpus World. Funksjonen compile-wumpus utfrer de ndvendige kallene for at lene i applikasjonen skal kompileres og loades. Vi ser at dette gjres p a en lett og elegant m ate.1 Tross alle fordelene er defsystem et system som gir oss kompabilitets-problemer siden applikasjonen forelpig kun er gjennomkjrbar p a MCL og ikke i Allegro (se seksjon 2.1). 2.3 Agenten Vi har tre viktige kontroll-funksjoner rundt agentens hjerne; selve agenten-funksjonen kgbagent, update-agent og implement-action. Disse funksjonene nner du i hhv kb-agent.lisp, utilities.cl og actions.cl. Vi betrakter den frste funksjonen i tillegg til kunnskapsbasen og slutningemekanismen (prolog) som agentens hjerne.Update-agent og implement-action benner seg i grenselandet mellom agentens hjerne og den fysiske verden siden dette er funksjoner som hhv tar i mot persepsjoner fra agentens sensorer i tillegg til a legge disse agent-objektets slot'er, og trigger agentens forsk p a handling. Update-agent kalles i oppstart-funksjonen wumpus-world og i implement-action . I start-funksjonen kalles den fordi agenten har et behov for a bli oppdatert i det hun blir \fdt" inn i spill-brettet (omgivelsen). Hun trenger a oppdatere sine persepsjoner (alts a setf'er persepsjonsvektoren til a inneholde persepsjonspredikatene som benner seg p a ruten), i tillegg til a vite om det er en Wumpus p a start-ruten. Er Wumpus p a start-ruten skal ikke agenten g a vidre, men i stedet skal livet termineres. Update-agent setter tilstanden til agenten. Dog ser det ut til at det er en bug i Franz' ACL fordi det virker som at funksjonen compile-system ignorerer key-word'et :silent, og dette pa tross av at det ikke er noen andre \key word argumenter" som overskriver :silent t. 1 Funksjonen kgb-agent er selve agenten som har sine del- og hovedm al. Det er en loop i denne funksjonen som g ar til agenten n ar sitt hovedm al, gir opp letingen eller dr. I innmaten av kgb-agent er det funksjonen make-percept-sentence-prol som oppdaterer kunnskapsbasen med persepsjonene agenten sanser. Deretter fanger agenten opp forslag til handling returnert av funksjonen make-action-query. Vi betrakter kodesnutten (setf action (make-action-query time)) som en del av viljen til agenten. make-action-sentence forteller kunnskapsbasen om handlingen som foretas. S a funksjonen kgb-agent kan betraktes som den delen av hjernen som legger ting til hukommelsen og henter ut beslutningene om valg av handling. Funksjonen implement-action er det leddet som knytter verden, de faktiske handlingene og fysiske lover, og agenten sammen. Den er, som sagt, en slags grenseland-funksjon fordi den tar inn agentens vilje som argument, mens den b ade sender impulser som stimulerer aksjonene (metodene) og formidler informasjon til brukeren (observatren) om hva som skjer. Vi betrakter metodene som fenomener i verden som avgjr om handlingene kan utfres eller ikke. Vi tenkte frst p a metodene som handlinger utfrt av eektorer, men fant at det ikke gir mening a snakke om eektorer siden det er en virtuell agent vi har med a gjre, og det bl.a er de fysiske lover og fenomener i verden som avgjr om en fysisk handling er gjennomfrbar. S a funksjonen trigger handlingene, og disse handlingene ligger i omgivelsen, utenfor agenten. Lrebokas presentasjon av agenter er at de returner en handling etter a ha r adfrt seg med kunnskapsbasen. V ar agent returnerer ikke en handling man den kaller en funksjon som tar seg av de tingene. Dermed vil v ar agent loope til den dr, gir opp eller n ar m alet. Dette er fordi vi betrakter agenten som en person som selv styrer sin kontinuitet av handlinger. For oss s a ser det ut som at forfatterne mener at agenten skal settes igang av noen utenforstande, noe annet enn sin egen vilje. xme 2.4 Omgivelsen Den visuelle representasjonen for oss som observatrer er spill-brettet. Det er funksjonen print-board som tar seg av utskrivingenn av brettet. I len pretty-printing.cl nner du de hjelpefunksjonene som print-board bruker. Alle rutene er objekter som kan inneholde predikater (sanseinntrykk) og andre objekter som monster og agenter. Print-board henter alts a ut nye verdier av objektene for hver gang den blir kalt. For en nrmere beskrivelse av typene nner du i seksjon 2.6 2.5 Kunnskapsbasen Til a representere kunnskaper om verden, bruker vi Paul Grahams implementasjon av Prolog i Lisp, fra Graham (1994) /refsubsec:di-prlog-pred. Vi har modisert noe p a koden med ider fra Norvig, for a kunne bruke noen hjelpefunksjoner og for a kunne samle alle instansieringer av variable i et sk, i en liste. Makroen \<-", som du nner i len on-lisp/prolog-till.cl, legger reglene i regelbasen *rules* (se dokumentasjonen til Paul Graham \On Lisp" for nrmere beskrivelse). 2.6 Typer og metoder Vi har valgt CLOS fordi det ligger nrt a tenke p a (eller kategorisere) ting som ulike typer av objekter. Filen wumpus-types.cl inneholder de globale variablene og alle klassene som er opprettet i systemet. Vi har en klasse for objektene som benner seg i spillet, gold og arrow (som er subklasser av object), vi har en agent klasse med player og wumpus som subklasser. Square er en klasse med wall som subklasse og en open-square som subklasser square, med subsubklasse pit. Filen actions.cl inneholder de metodene p a klassene som spillet gjr bruk av. Disse metodene er faktiske handlinger som agenten utfrer. Metodene trigges av dispatch-funksjonen implement-action. 2.7 Kommunikasjon mellom agenten, KB og brukeren Det er to funksjoner som kommuniserer med brukeren; det ene er agent-funksjonen selv: kgbagent, som forteller hva hun sanser, og den andre er implement-action som holder brukeren oppdatert p a hva som skjer i tillegg til a trigge handlings-metodene og skrive ut spill-brettet. 2.8 Miscellaneous Koordinatsystemene. Representasjonen av koordinatene til spill-brettet i array'et og i kunnskapsbasen er forskjellige. Aref bruker det kartesiske koordinatsystemet. Spill-brettet representeres ogs a som et 2-dimensjonalt kartesisk koordinatsystem, men skiller seg fra arrayrepresentasjonen ved at det 2-dimensjonale array'et er rotert 90 grader \clockwise". Derfor er f.eks koordinatene til agentens startposisjon, *start*, i array'et listen '(4 1), mens den samme posisjonen i det visuelle spill-brettet '(1 1). Dette er mulig fordi vi har et skille mellom det fysiske og logiske spr aket (jfr seksjon ??). Vi valgte a ikke abstrahere aref for a bruke den rotasjonen vi er vant med (dvs x-aksen peker mot st og y-aksen peker nordover, hvis koordinatsystem vi har brukt under den visuelle representasjonen brettet) selv om vi lett kunne gjort det med mappings-funksjoner som ser ut ala: (defun map-to-cartesian (i s) "returns the cartesian cooridinates by rotate the coord-system 90 degrees ``counter-clockwise''" (let ((x s) (y (- (+ 1 *row-length*) i))) (list x y))) (defun map-from-cartesian (x y) "returns the coordinates according to Common Lisp arrays, rotating the cartesian 90 degrees clockwise" (let ((i (- (+ 1 *row-length*) y)) (s x)) (list i s))) Statistikk. I len stat-lisp.cl inneholder funksjonen make-statistics og infrastruktur for to agenter ulike kvaliteter. Man kan for morroskyld se hvilken av disse agenten som gjor det best p a spill-brettet tatt i betrakting deres ulike egenskaper. 2.9 Eektivisering og optimering Prolog-implementasjon i Lisp vi benytter som slutningsmekanisme bruker ganske lang tid. Muligens ville Prolog-kompilatoren til Norvig i PAIP vrt raskere. Denne har vi imidlertid heller ikke f att til a fungere. Prolog-versjonen i On Lisp brukte Line Bergem under eksamen i SLI 330, s a denne visste vi fungerte. Prolog-versjonen til Graham best ar av mindre og kode enn Norvigs, og den er derfor lettere og forst a. Iflge Graham (On Lisp, s. 346 og 347) er hans versjon en kompilator p a et vis, den oversetter regler til Lisp funksjoner Og Common Lisp oversetter til maskinkode. Men den er ikke en full Prolog-kompilator, fordi kompilering gjres av en Lisp-kompilator som ser etter optimering av Lisp og ikke av Prolog. Da vi tok i bruk Paul Grahams kode fra On Lisp visste vi ikke at det var ndvendig a optimere rekursive funksjoner til hale-rekursive. Vi kk noen feil-meldinger fra ACL om at stacken var full. Underveis i debug-ingen foretok vi en rekke optimerings- og eektiviseringstiltak for a kunne forst a hva den overnevnte feilen l a i, og hva vi kunne gjre for a overvinne slike feil. Det viste seg at et kall p a (gc) ikke ville hjelpe siden garbage-collector ikke opererer p a stack'en, men p a heap'en. Derfor var en kning av heap'en ikke noen lsning for v art stack-problem. Vi har likevel kt heap-size med en million bytes for kjring p a Dec-stasjon. Vi testet systemet p a Macintosh og fant at MCL (Macintosh Common LISP) ikke har de problemer med stack overow som ACL har fordi MCL alltid optimerer hale-kall. Vi har sett at MCL gc'er hele tiden. (Se seksjon 2.1 for beskrivelse av hvordan applikasjonen kjres p a Macintosh). I de flgende avsnittene skal vi ta for oss de eektiviserings- og optimerings-tiltakene vi har foretatt i Wumpus World. Seksjon 1.3.7 viser vi hvordan vi bygger et image for unix-versjonen av applikasjonen, og seksjon 2.9.2 omhandler v are optimerings-forsk. 2.9.1 Eektiviserings-tiltak Eektivisering p a DEC. Et av eektiviserings-tiltakene vi har foretatt er a bygge et image for systemet. N ar et slik image er bygget, kan man dumpe lisp'en. Dette gjr at n ar vi starter systemet med det allerede dumpede imaget, slipper vi a kompilere og loade alle de lene systemet bruker. Med fuksjonen (som du ogs a nner i len system.cl; (build-lisp-image "wumpus.dxl" :include-compiler t :c-heap-size 167108864 :lisp-heap-size 167108864) bygger vi et image "wumpus.dxl" med kt heap-size b ade c-heap'en og lisp-heap'en. Utfrer vi en (dumplisp :name "dumpy.cl") etter at systemets kompilerte ler er loadet, slipper vi a loade og kompilere ler ved neste oppstart av systemet. Vi kaller da p a lisp'en med lisp: lisp -I dumpy.dxl Eektivisering p a MAC og Windows Ved a kompilere lene og laste inn disse vil systemet kjre raskere enn n ar man kjrer kilde-len fordi i siste tilfelle vil interpreteren oppfre seg som den aldri har sett funksjonene fr. 2.9.2 Optimerings-tiltak Optimering av hale-rekursive kall Vi hadde store problemer med a fa Allegro Common Lisp til a kjre spillet v art. Programmet knelte allerede n ar agenten hadde gjort svrt f a handliner p a grunn av flgende melding Error: Stack overflow (signal 1000) [condition type: synchronous-operating-system-signal] Vi skjnte at stacken ikke ble poppet, men visste ikke hvorfor. Ved grundigere lesning i On Lisp-boken til Graham (s. 396), fant vi at kontinuasjonsmakrne, som brukes i hans Lispimplementasjon av Prolog, avhenger sterkt av optimalisering av hale-kall, for at det ikke skal g a tom for plass p a stacken. Vi gjorde en del forsk p a a f a Allegro til a optimere hale-kall, og nedenfor beskriver vi optimerings-funksjonene. Funksjonene comp:tail-call-self-merge-switch og comp:tail-call-non-self-merge-switch vil, i flge ACL-manualen, optimerimere rekursive funksjoner til halerekursive (under gitte omstendigheter), som vi vet er mye mer eektive n ar antall rekursive kall blir veldig stort. Funsjonene som her setf'es nner du i system-len (setf comp:tail-call-self-merge-switch #'(lambda (safety speed debug space) (declaim (optimize (safety 1) (speed 3) (debug 1) (space 3)))) comp:tail-call-non-self-merge-switch #'(lambda (safety speed debug space) (declaim (optimize (safety 1) (speed 3) (debug 1) (space 3))))) Mye arbeid skulle til for a f a koden til a kjre, men etter at vi fant funksjonen peepholeoptimize-switch, og skrudde den p a var agenten i stand til a g a opptil 11 steg, og dette ser ut til a vre avhengig av belastningen lispen utsettes for n ar et sk etter beste handling m a ta i betraktning om agenten sanser noe i de ulike rommene hun beveger seg i. comp:peephole-optimize-switch #'(lambda (safety speed debug space) (declaim (optimize (safety 1) (speed 2) (debug 1) (space 3))))) peephole-optimize-switch nnes i compiler-pakken og srger for at undvendige og overdige instruksjoner fjernes. Tillegg A Wumpus World |kildekode A.1 wumpus-types.cl ;; ---------------------- globale variable ---------------------------(defvar *board* nil) (defparameter *start* '(4 1)) (defconstant *row-length* 4) ;; ---------------------- klasser og objekter -------------------------(defclass object () ((name :accessor name :initarg :obj-name :initform nil))) (defclass gold (object) ((name :accessor name :initarg :obj-name :initform "gold") (predicate :accessor predicate :initform 'glitter))) (defclass arrow (object) ((name :accessor name :initarg :obj-name :initform "arrow"))) (defclass agent () ((name :accessor name :initarg :ag-name :initform nil) (alive :accessor alive :initform t))) (defclass player (agent) ((facing :accessor facing :initform 'east) (possesions :accessor possesions :initform (list (make-instance 'arrow))) (percepts :accessor percepts :initform (make-array 5 :initial-element 'none)))) (defclass wumpus (agent) 27 ((name :accessor name :initform "wumpus"))) (defclass square () ()) (defclass wall (square) ()) (defclass open-square (square) ((agents :accessor agents :initarg sq-agents :initform nil) (contents :accessor contents :initarg sq-contents :initform nil) (predicates :accessor predicates :initarg sq-predicates :initform nil))) (defclass pit (open-square) ()) A.2 kb-agent.lisp (defun kgb-agent (agent) "Define an action-value knowledge-based agent." (let ((points 0) (time 0) (action (gensym "?"))) (loop until (or (not (alive agent)) (eq action 'give-up)) do (let ((percept (percepts agent))) (tell (make-percept-sentence-prol percept time)) (format t "~2& My perceptions at time ") (display-universal-time) (format t "are/is ~{~a, ~} and the number in my action sequence is ~a. I'm facing ~a. ~&" (list-perceptions percept) time (facing agent)) (gc) (setf action (make-action-query time)) (implement-action agent action) (when (eq action 'grab) (change-strategy agent)) (cond ((eq action 'climb) (setf points (+ points 1000)) (loop-finish)) (:else (decf points))) (tell (make-action-sentence `',action time)) (incf time) (let ((location (make-location-query time)) (orientation (make-orientation-query time))) (tell-first `(at 'dumpy ,location ,time)) (tell-first `(orientation 'dumpy ,orientation ,time))) (when (not (alive agent)) (setf points (- points 10000)) (loop-finish))) finally (return points)))) A.3 system.cl (defsystem :wumpus-world (:pretty-name "An agent killing the Wumpus" :default-pathname #p"A:/Wumpus-Windows/" :default-package :wumpus :default-file-type "cl") (:module-group "optimize" (:serial "Wumpus-system/optimizing")) (:serial (:module-group "wumpus-types" (:serial "Wumpus-system/wumpus-types" "Wumpus-system/kb-agent")) (:module-group "graphics" (:serial "Wumpus-system/pretty-printing") (:in-order-to (:compile) (:compile "wumpus-types"))) (:module-group "utilities" (:parallel "Wumpus-system/init" "Wumpus-system/utilities" "Wumpus-system/abstractions")) (:module-group "actions" (:serial "Wumpus-system/actions")) (:module-group "prolog-tools" (:parallel "onlisp/prolog-till" "onlisp/onprolog3") (:compile-satisfies-load t)) (:module-group "knowledge" (:serial "Wumpus-system/wumpus-rules" "Wumpus-system/action-value" "Wumpus-system/new-values")))) (defun compile-wumpus () "compile-function for the Wumpus World. Only used before dumping the image." (compile-system :wumpus-world :silent t :recompile t)) (defun wumpus-world () "start function for the Wumpus World" (setf *board* (initboard 6)) (clear-db) (load #p"A:/Wumpus-Windows/Wumpus-system/action-value.fasl") (load #p"A:/Wumpus-Windows/Wumpus-system/wumpus-rules.fasl") (let* ((coord (find-in-array 'player 1 1 *row-length*)) (start-square (aref *board* (x-coord coord) (y-coord coord)))) (update-agent dumpy start-square) (format t " ~2& [Live data follows..] ~2&") (print-board) (kgb-agent dumpy))) (defun change-strategy (agent) (let* ((coordinates (find-in-array 'player 1 1 *row-length*)) (location (aref *board* (x-coord coordinates) (y-coord coordinates)))) (if (equal *start* location) (implement-action 'climb) (progn (clear-predicates 'great) (clear-predicates 'good) (clear-predicates 'medium) (load #p"A:/Wumpus-Windows/Wumpus-system/new-values.fasl"))))) (defun c () (compile-wumpus)) (defun w () (wumpus-world)) ;;;(show-system :wumpus-world) A.4 optimizing.cl (build-lisp-image "wumpus.dxl" :include-compiler t :c-heap-size 167108864 :lisp-heap-size 167108864) (defpackage "wumpus-game" (:nicknames "wumpus") (:use "EXCL" "USER" "CLOS" "DEFSYSTEM") (:implementation-packages "DEFSYSTEM" "USER")) (setf comp:tail-call-self-merge-switch #'(lambda (safety speed debug space) (declaim (optimize (safety 1) (speed 2) (debug 1) (space 3)))) comp:tail-call-non-self-merge-switch #'(lambda (safety speed debug space) (declaim (optimize (safety 1) (speed 2) (debug 1) (space 3)))) comp:peephole-optimize-switch #'(lambda (safety speed debug space) (declaim (optimize (safety 1) (speed 2) (debug 1) (space 3))))) (ensure-generic-function 'comp:tail-call-self-merge-switch :environment 'compile-time) ;;Error: Attempt to make a function definition for the name ;;compiler:tail-call-self-merge-switch. ;;This name is in the COMPILER package and defining it is ;;a violation for portable programs. ;;The package COMPILER has package-definition-lock set, ;;which causes the system to signal this violation. ;;[condition type: package-locked-error] A.5 actions.cl (defmethod shoot ((ag player)) (let ((arrow (find-type 'arrow (possesions ag)))) (when arrow (setf (possesions ag) (remove arrow (possesions ag))) (let* ((dir (facing ag)) (coord (find-in-array 'player 1 1 *row-length*)) (new-coord (next-coord dir (x-coord coord) (y-coord coord)))) (find-target ag dir (x-coord new-coord) (y-coord new-coord)))))) (defun find-target (agent dir i s) (let ((square (aref *board* i s))) (when (not (typep square 'wall)) (let ((monster (find-type 'wumpus (contents square)))) ;evt Wumpus (if monster (kill agent monster) (let ((new-coord (next-coord dir i s))) (find-target agent dir (x-coord new-coord)(y-coord new-coord)))))))) (defmethod kill ((ag1 player) (ag2 wumpus)) "ag1 kills ag2 and updates the play-board " (let* ((coord (find-in-array 'player 1 1 *row-length*)) (square (aref *board* (x-coord coord) (y-coord coord)))) (remove ag2 (contents square)) (setf (scream (percepts ag1)) 'scream) (setf (alive ag2) nil))) (defmethod go-forward ((ag player)) (let* ((coord (find-in-array 'player 1 1 *row-length*)) (old-square (aref *board* (x-coord coord) (y-coord coord))) (dir (facing ag)) (next-square (next-square dir (x-coord coord) (y-coord coord)))) (unless (typep next-square 'wall) (push ag (contents next-square)) (setf (contents old-square) (remove ag (contents old-square)))))) (defmethod go-forward ((ag player)) (let* ((coord (find-in-array 'player 1 1 *row-length*)) (dir (facing ag)) (old-square (aref *board* (x-coord coord) (y-coord coord))) (next-square (next-square dir (x-coord coord) (y-coord coord)))) (unless (typep next-square 'wall) (push ag (contents next-square)) (setf (contents old-square) (remove ag (contents old-square)))))) (defmethod turn-right ((ag player)) "oppdaterer agentens retning (slot'en facing) naar han roterer mot hoeyre." (case (facing ag) (north (setf (facing ag) 'east)) (south (setf (facing ag) 'west)) (east (setf (facing ag) 'south)) (west (setf (facing ag) 'north)))) (defmethod turn-left ((ag player)) "oppdaterer agentens retning (slot'en facing) naar han roterer mot venstre." (case (facing ag) (north (setf (facing ag) 'west)) (south (setf (facing ag) 'east)) (east (setf (facing ag) 'north)) (west (setf (facing ag) 'south)))) (defmethod grab ((ag player) object) (let* ((coord (find-in-array 'player 1 1 *row-length*)) (square (aref *board* (x-coord coord) (y-coord coord))) (obj (find-type object (contents square)))) (when obj (push obj (possesions ag)) (setf (contents square) (remove obj (contents square)) (predicates square) (remove (predicate obj) (predicates square)))))) (defmethod climb ((ag player)) (let ((start-square (aref *board* (x-coord *start*) (y-coord *start*)))) (setf (contents start-square) (remove ag (contents start-square))) (clear-db))) (defun implement-action (agent action) (let* ((coord (find-in-array 'player 1 1 *row-length*)) (square (aref *board* (x-coord coord) (y-coord coord))) (monster (find-type 'wumpus (contents square))) (direction (facing agent)) (next-square (next-square direction (x-coord coord) (y-coord coord)))) (ccase action (left (turn-left agent) (format t "~& (Your agent is turning left.) ~2&") (print-board)) (right (turn-right agent) (format t "~& (Your agent is turning right.) ~2&") (print-board)) (forward (format t "~& (Your agent is walking forward.) ~2&") (cond ((member 'bump (list-perceptions (percepts agent))) (format t " ( ..she is bumping her head onto the wall!) ~2&")) ((typep next-square 'pit) (format t " (Your agent fell into the Pit.) ~&")) ((and monster (alive wumpus)) (format t " (Your agent is killed by Wumpi.) ~&"))) (go-forward agent) (update-agent agent next-square) (print-board)) (shoot (format t " ~& (Your agent is trying to shoot the Wumpus..)~%") (if (find-type 'arrow (possesions agent)) (progn (shoot agent) (if (alive wumpus) (format t " (She missed.. sorry!) ~&") (format t " (She killed it!) ~2&"))) (format t " (Sorry, she is out of arrows.) ~&")) (print-board)) (climb (format t "~& (She is climbing out of this world..) Congratulation! Your agent has now reached her goal. In the new version of the game, you can climb to the next level. Until then, BYE! ~2&") (climb agent) (print-board) (return-from implement-action)) (give-up (format t "~& Your agent is a looser! ~%")) (grab (grab agent 'gold) (format t " She is grabbing the gold.. Yes! Your agent has it, and is changing strategy, to go home! ~2&") (print-board))))) A.6 action-value.cl ;;; nytteverdier paa konsekvensen av handlingene. ;;; deadly og risky (og shoot) gjelder uansett, ;;; resten er avhengig om agenten har gull eller ikke. (<- (risky 'forward ?time) (and (at 'dumpy ?location ?time) (locationahead 'dumpy ?ahead ?time) (not (deadly 'forward ?time)) (not (ok ?ahead))) (cut)) (<- (deadly 'forward ?time) (and (at 'dumpy ?location ?time) (loacationahead 'dumpy ?ahead ?time) (or (pit ?ahead) (at 'Wumpus ?ahead) (not (dead 'Wumpus)))) (cut)) (<- (great 'shoot ?time) (and (holding 'arrow) (at 'dumpy ?location ?time) (locationahead 'dumpy ?ahead ?time) (at 'Wumpus ?ahead) (and (right-of 'dumpy ?location-right ?time) (or (wall ?location-right) (not (ok ?location-right)) (visited ?location-right))) (and (left-of 'dumpy ?location-left ?time) (or (wall ?location-left) (not (ok ?location-left)) (visited ?location-left))))) ;; hvis valget staar mellom aa snu, og aa skyte aa satse paa aa treffe wumpusen, ;; er det lurest aa skyte (<- (great 'shoot ?time) (and (holding 'arrow) (at 'dumpy ?location ?time) (smelly ?location) (previously-visited 'dumpy ?prev ?pt ?time) (entered ?prev ?pt ?ent) (second-visit ?prev ?ent))) ;; skyte er likestilt aa snu seg, hvis agenten har kommet til et sted det lukter (<- (medium 'shoot ?time) (and (holding 'arrow) (at 'dumpy ?location ?time) (smelly ?location) (locationahead 'dumpy ?ahead ?time) (not (visited ?ahead)))) (<- (great 'grab ?time) (and (at 'gold ?location ?time) (not (holding 'gold)))) (<- (good 'forward ?time) (and (at 'dumpy ?location ?time) (locationahead 'dumpy ?ahead ?time) (not (wall ?ahead)) (not (visited ?ahead)) (ok ?ahead))) (<- (medium 'forward ?time) (and (at 'dumpy ?location ?time) (locationahead 'dumpy ?ahead ?time) (not (wall ?ahead)) (visited ?ahead))) (<- (medium 'left ?time) (and (at 'dumpy ?location ?time) (locationahead 'dumpy ?ahead ?time) (not (ok ?ahead)) (previously ?time ?previous-time) (not (action 'right ?previous-time)) (not (action 'shoot ?previous-time)) (left-of 'dumpy ?location-left ?time) (not (wall ?location-left)))) (<- (good 'left ?time) (and (at 'dumpy ?location ?time) (locationahead 'dumpy ?ahead ?time) (wall ?ahead) (previously ?time ?previous-time) (not (action 'right ?previous-time)) (not (action 'shoot ?previous-time)) (left-of 'dumpy ?location-left ?time) (not (wall ?location-left)) (not (visited ?location-left)))) (<- (medium 'left ?time) (and (at 'dumpy ?location ?time) (locationahead 'dumpy ?ahead ?time) (wall ?ahead) (previously ?time ?previous-time) (not (action 'right ?previous-time)) (not (action 'shoot ?previous-time)) (left-of 'dumpy ?location-left ?time) (not (wall ?location-left)) (visited ?location-left))) (<- (good 'left ?time) (and (at 'dumpy ?location ?time) (locationahead 'dumpy ?ahead ?time) (visited ?ahead) (previously ?time ?previous-time) (not (action 'right ?previous-time)) (not (action 'shoot ?previous-time)) (left-of 'dumpy ?location-left ?time) (not (wall ?location-left)) (not (visited ?location-left)) (ok ?location-left))) (<- (medium 'right ?time) (and (at 'dumpy ?location ?time) (locationahead 'dumpy ?ahead ?time) (not (ok ?ahead)) (previously ?time ?previous-time) (not (action 'left ?previous-time)) (not (action 'shoot ?previous-time)) (right-of 'dumpy ?location-right ?time) (not (wall ?location-right)))) (<- (good 'right ?time) (and (at 'dumpy ?location ?time) (locationahead 'dumpy ?ahead ?time) (wall ?ahead) (previously ?time ?previous-time) (not (action 'left ?previous-time)) (not (action 'shoot ?previous-time)) (right-of 'dumpy ?location-right ?time) (not (wall ?location-right)) (not (visited ?location-right)))) (<- (medium 'right ?time) (and (at 'dumpy ?location ?time) (locationahead 'dumpy ?ahead ?time) (wall ?ahead) (previously ?time ?previous-time) (not (action 'left ?previous-time)) (not (action 'shoot ?previous-time)) (right-of 'dumpy ?location-right ?time) (not (wall ?location-right)) (visited ?location-right))) (<- (good 'right ?time) (and (at 'dumpy ?location ?time) (locationahead 'dumpy ?ahead ?time) (visited ?ahead) (previously ?time ?previous-time) (not (action 'left ?previous-time)) (not (action 'shoot ?previous-time)) (right-of 'dumpy ?location-right ?time) (not (wall ?location-right)) (not (visited ?location-right)) (ok ?location-right))) (<- (good 'forward ?time) (and (at 'dumpy ?location ?time) (risky 'forward ?time) (previously-visited 'dumpy ?prev ?pt ?time) (entered ?prev ?pt ?ent) (second-visit ?prev ?ent))) (<- (good 'give-up ?time) (and (at 'dumpy ?location ?time) (deadly 'forward ?time) (previously-visited 'dumpy ?prev ?pt ?time) (entered ?prev ?pt ?ent) (second-visit ?prev ?ent))) A.7 wumpus-rules.cl ;;; Kunnskapsbasen ;;; fakta om Wumpus-verden ;; setning om tid (<- (previously ?s1 ?s2) (is ?s2 (- ?s1 1)) (cut)) ;;; holding, holder rede paa om 'dumpy holder et objekt til enhver tid (<- (holding ?object) (and (at ?object ?location ?s) (action 'grab ?s) (at 'dumpy ?location ?s))) (<- (holding 'arrow) (not (action 'shoot _))) ;; setninger om lokalisering (<- (at 'dumpy (1 1) 0) (cut)) (<- (orientation 'dumpy 0 0) (cut)) ;;;locationtoward, finner neste rute mhp retning (<- (locationtoward (?x ?y) 270 (?ut1 ?ut2)) (and (is ?ut1 ?x) (is ?ut2 (- ?y 1)))) (<- (locationtoward (?x ?y) 0 (?ut1 ?ut2)) (and (is ?ut1 (+ ?x 1)) (is ?ut2 ?y))) (<- (locationtoward (?x ?y) 180 (?ut1 ?ut2))(and (is ?ut1 (- ?x 1)) (is ?ut2 ?y))) (<- (locationtoward (?x ?y) 90 (?ut1 ?ut2)) (and (is ?ut1 ?x) (is ?ut2 (+ ?y 1)))) ;;; adjacent finner omliggende ruter. (<- (adjacent ?l1 ?l2) (locationtoward ?l1 ?o ?l2)) (<- (locationahead ?agent ?ut2 ?s) (and (at ?agent ?ut1 ?s) (orientation ?agent ?o ?s) (locationtoward ?ut1 ?o ?ut2) (cut))) (<- (wall (?x ?y)) (or (lisp (< ?x 1)) (lisp (> ?x *row-length*)) (lisp (< ?y 1)) (lisp (> ?y *row-length*)))) (<- (not-forward ?action ?s) (and (action ?action ?s) (or (lisp (equal ?action 'left)) (lisp (equal ?action 'right)) (lisp (equal ?action 'shoot)) (lisp (equal ?action 'grab)) (lisp (equal ?action 'give-up))))) (<- (at 'dumpy ?location ?s1) (and (action 'forward ?s0) (is ?s1 (+ ?s0 1)) (locationahead 'dumpy ?location ?s0) (not (wall ?location)))) (<- (at 'dumpy ?location ?s1) (and (not-forward ?action ?s0) (is ?s1 (+ ?s0 1)) (at 'dumpy ?location ?s0))) (<- (entered ?l ?s ?e) (at 'dumpy ?l ?s) (is ?s0 (- ?s 1)) (not (at 'dumpy ?l ?s0)) (is ?e ?s) (cut)) (<- (entered ?l ?s ?e) (is ?s0 (- ?s 1)) (entered ?l ?s0 ?e)) (<- (homewards (?x1 ?y1) (?x2 ?y2)) (and (lisp (<= ?x2 ?x1)) (lisp (<= ?y2 ?y1)))) (<- (orientation ?agent ?x 0) (cut) (fail)) ;nulltest (maa stoppe paa 0). (<- (orientation ?agent ?o1 ?s1) (and (previously ?s1 ?s0) (action 'left ?s0) (orientation ?agent ?o0 ?s0) (is ?o1 (mod (+ ?o0 90) 360)) (cut))) (<- (orientation ?agent ?o1 ?s1) (and (previously ?s1 ?s0) (action 'right ?s0) (orientation ?agent ?o0 ?s0) (is ?o1 (mod (- ?o0 90) 360)) (cut))) (<- (orientation ?agent ?o1 ?s1) (and (previously ?s1 ?s0) (not (action 'right ?s0)) (not (action 'left ?s0)) (orientation ?agent ?o1 ?s0) (cut))) (<- (left-of ?agent ?neighbour ?time) (and (at ?agent ?location ?time) (orientation ?agent ?degrees ?time) (is ?degrees1 (mod (+ ?degrees 90) 360)) (locationtoward ?location ?degrees1 ?neighbour))) (<- (right-of ?agent ?neighbour ?time) (and (at ?agent ?location ?time) (orientation ?agent ?degrees ?time) (is ?degrees1 (mod (- ?degrees 90) 360)) (locationtoward ?location ?degrees1 ?neighbour))) (<- (opposit-dir ?dir ?opp-dir) (is ?opp-dir (mod (+ ?dir 180) 360))) (<- (back-of ?agent ?behind ?time) (and (at ?agent ?location ?time) (orientation ?agent ?o ?time) (opposit-dir ?o ?opp-dir) (locationtoward ?location ?opp-dir ?behind))) (<- (visited ?location) (at 'dumpy ?location ?time)) (<- (visited-before ?location -1) (cut) (fail)) (<- (visited-before ?location ?time) (is ?prev (- ?time 1)) (at 'dumpy ?location ?prev) (cut)) (<- (visited-before ?location ?time) (is ?prev (- ?time 1)) (visited-before ?location ?prev)) ;; second sier fra om en agent besker en rute for andre gang (<- (second-visit ?location ?enter-time) (visited-before ?location ?enter-time) (is ?time (- ?enter-time 1)) (or (and (locationtoward ?location 0 ?l1) (at 'dumpy ?l1 ?time) (orientation 'dumpy 180 ?time) (action 'forward ?time)) (and (locationtoward ?location 90 ?l1) (at 'dumpy ?l1 ?time) (orientation 'dumpy 270 ?time) (action 'forward ?time)) (and (locationtoward ?location 180 ?l1) (at 'dumpy ?l1 ?time) (orientation 'dumpy 0 ?time) (action 'forward ?time)) (and (locationtoward ?location 270 ?l1) (at 'dumpy ?l1 ?time) (orientation 'dumpy 90 ?time) (action 'forward ?time)))) (<- (previously-visited ?agent ?location (<- (previously-visited ?agent ?location (previously ?time ?previous-time) (action 'forward ?previous-time) (at ?agent ?location ?previous-time) (<- (previously-visited ?agent ?location (previously ?time ?previous-time) (previously-visited ?agent ?location ;; skjulte egenskaper ved verden ?previous-time 0) (cut) (fail)) ?previous-time ?time) (cut)) ?prev ?time) ?prev ?previous-time)) (<- (smelly ?location) (and (percept ('stench _ _ _ _) ?s) (at 'dumpy ?location ?s) (cut))) (<- (breezy ?location) (and (percept (_ 'breeze _ _ _) ?s) (at 'dumpy ?location ?s) (cut))) (<- (glittery ?location ?s) (and (percept (_ _ 'glitter _ _) ?s) (at 'dumpy ?location ?s) (cut))) (<- (bumpy ?location) (and (percept (_ _ _ 'bump _) ?s) (at 'dumpy ?location ?s) (cut))) (<- (noisy ?location) (and (percept (_ _ _ _ 'scream) ?s) (at 'dumpy ?location ?s) (cut))) (<- (at 'gold ?location ?s) (glittery ?location ?s)) (<- (dead 'Wumpus) (percept (_ _ _ _ 'scream) ?s)) (<- (not-wumpus ?location) (and (adjacent ?location ?neighbour) (visited ?neighbour) (not (smelly ?neighbour)))) ;;;wumpus er i samme rute til enhver tid (<- (at 'Wumpus ?location) (not (wall ?location)) (and (adjacent ?location ?neighbour) (smelly ?neighbour) (and (locationtoward ?neighbour 0 ?L1) (or (lisp (equal ?L1 ?location)) (wall ?L1) (not-wumpus ?L1))) (and (locationtoward ?neighbour 90 ?L2) (or (lisp (equal ?L2 ?location)) (wall ?L2) (not-wumpus ?L2))) (and (locationtoward ?neighbour 180 ?L3) (or (lisp (equal ?L3 ?location)) (wall ?L3) (not-wumpus ?L3))) (and (locationtoward ?neighbour 270 ?L4) (or (lisp (equal ?L4 ?location)) (wall ?L4) (not-wumpus ?L4)))) (cut)) (<- (at 'Wumpus ?location) (not (wall ?location)) (and (and (locationtoward ?location 0 ?l1) (or (smelly ?l1) (wall ?l1))) (and (locationtoward ?location 90 ?l2) (or (smelly ?l2) (wall ?l2))) (and (locationtoward ?location 180 ?l3) (or (smelly ?l3) (wall ?l3))) (and (locationtoward ?location 270 ?l4) (or (smelly ?l4) (wall ?l4))))) (<- (not-pit ?location) (and (adjacent ?location ?neighbour) (visited ?neighbour) (not (breezy ?neighbour)))) (<- (pit ?location) (and (adjacent ?location ?neigbour) (breezy ?neighbour) (and (locationtoward ?neighbour 0 ?l1) (or (lisp (equal ?l1 ?location)) (wall ?l1) (not-pit ?l1))) (and (locationtoward ?neighbour 90 ?l2) (or (lisp (equal ?l2 ?location)) (wall ?l2) (not-pit ?l2))) (and (locationtoward ?neighbour 180 ?l3) (or (lisp (equal ?l3 ?location)) (wall ?l3) (not-pit ?l3))) (and (locationtoward ?neighbour 270 ?l4) (or (lisp (equal ?l4 ?location)) (wall ?l4) (not-pit ?l4))))) (<- (pit ?location) (and (and (locationtoward ?location 0 ?l1) (or (breezy ?l1) (wall ?l1))) (and (locationtoward ?location 90 ?l2) (or (breezy ?l2) (wall ?l2))) (and (locationtoward ?location 180 ?l3) (or (breezy ?l3) (wall ?l3))) (and (locationtoward ?location 270 ?l4) (or (breezy ?l4) (wall ?l4))))) (<- (ok ?location) (and (not-pit ?location) (or (not-wumpus ?location) (dead 'Wumpus))) (cut)) (<- (ok ?location) (visited ?location)) ;; denne brukes ikke (<- (possible-dead-end ?l ?time) (and (at 'dumpy ?l ?time) (entered ?l ?time ?ent) (left-of 'dumpy ?left ?ent) (or (wall ?left) (not (ok ?left))) (right-of 'dumpy ?right ?ent) (or (wall ?right) (not (ok ?right))) (locationahead 'dumpy ?ahead ?ent) (or (wall ?ahead) (not (ok ?ahead)))) (cut)) (<- (possible-dead-end ?l ?time) (and (at 'dumpy ?l ?time) (previously-visited 'dumpy ?prev ?pt ?time) (entered ?prev ?pt ?ent) (possible-dead-end ?prev ?ent) (is ?act-time (+ ?pt 1)) (or (and (left-of 'dumpy ?left ?act-time) (or (wall ?left) (visited ?left)) (or (and (locationahead 'dumpy ?ahead ?act-time) (or (wall ?ahead) (visited ?ahead))) (and (right-of 'dumpy ?right ?act-time) (or (wall ?right) (visited ?right))))) (and (locationahead 'dumpy ?ahead ?act-time) (or (wall ?ahead) (visited ?ahead)) (right-of 'dumpy ?right ?act-time) (or (wall ?right) (visited ?right)))))) A.8 new-values.cl ;; naar agenten har funnet gullet maa hun skifte strategi (<- (great 'climb ?time) (at 'dumpy (1 1) ?time)) (<- (good 'forward ?time) (and (at 'dumpy ?location ?time) (locationahead 'dumpy ?ahead ?time) (homewards ?location ?ahead) (not (wall ?ahead)) (ok ?ahead))) (<- (medium 'forward ?time) (and (at 'dumpy ?location ?time) (locationahead 'dumpy ?ahead ?time) (not (homewards ?location ?ahead)) (not (wall ?ahead)) (ok ?ahead))) (<- (good 'left ?time) (and (at 'dumpy _ ?time) (or (orientation 'dumpy 90 ?time) (orientation 'dumpy 180 ?time)) (left-of 'dumpy ?left ?time) (ok ?left) (not (good 'forward ?time)))) (<- (medium 'left ?time) (and (at 'dumpy ?location ?time) (locationahead 'dumpy ?ahead ?time) (not (ok ?ahead)) (left-of 'dumpy ?location-left ?time) (not (wall ?location-left)))) (<- (good 'right ?time) (and (at 'dumpy _ ?time) (or (orientation 'dumpy 0 ?time) (orientation 'dumpy 270 ?time)) (right-of 'dumpy ?right ?time) (ok ?right) (not (good 'forward ?time)))) (<- (medium 'right ?time) (and (at 'dumpy ?location ?time) (locationahead 'dumpy ?ahead ?time) (not (ok ?ahead)) (right-of 'dumpy ?location-right ?time) (not (wall ?location-right)))) A.9 chicke-value.cl ;;; nytteverdier paa konsekvensen av handlingene. ;;; deadly og risky (og shoot) gjelder uansett, ;;; resten er avhengig om agenten har gull eller ikke. (<- (risky 'forward ?time) (and (at 'dumpy ?location ?time) (locationahead 'dumpy ?ahead ?time) (not (deadly 'forward ?time)) (not (ok ?ahead))) (cut)) (<- (deadly 'forward ?time) (and (at 'dumpy ?location ?time) (loacationahead 'dumpy ?ahead ?time) (or (pit ?ahead) (at 'Wumpus ?ahead) (not (dead 'Wumpus)))) (cut)) (<- (great 'shoot ?time) (and (holding 'arrow) (at 'dumpy ?location ?time) (locationahead 'dumpy ?ahead ?time) (at 'Wumpus ?ahead) (and (right-of 'dumpy ?location-right ?time) (or (wall ?location-right) (not (ok ?location-right)) (visited ?location-right))) (and (left-of 'dumpy ?location-left ?time) (or (wall ?location-left) (not (ok ?location-left)) (visited ?location-left))))) (<- (great 'shoot ?time) (and (holding 'arrow) (at 'dumpy ?location ?time) (smelly ?location) (previously-visited 'dumpy ?prev ?pt ?time) (entered ?prev ?pt ?ent) (second-visit ?prev ?ent))) (<- (medium 'shoot ?time) (and (holding 'arrow) (at 'dumpy ?location ?time) (smelly ?location) (locationahead 'dumpy ?ahead ?time) (not (visited ?ahead)))) (<- (great 'grab ?time) (and (at 'gold ?location ?time) (not (holding 'gold)))) (<- (good 'forward ?time) (and (at 'dumpy ?location ?time) (locationahead 'dumpy ?ahead ?time) (not (wall ?ahead)) (not (visited ?ahead)) (ok ?ahead))) (<- (medium 'forward ?time) (and (at 'dumpy ?location ?time) (locationahead 'dumpy ?ahead ?time) (not (wall ?ahead)) (visited ?ahead))) (<- (medium 'left ?time) (and (at 'dumpy ?location ?time) (locationahead 'dumpy ?ahead ?time) (not (ok ?ahead)) (previously ?time ?previous-time) (not (action 'right ?previous-time)) (not (action 'shoot ?previous-time)) (left-of 'dumpy ?location-left ?time) (not (wall ?location-left)))) (<- (good 'left ?time) (and (at 'dumpy ?location ?time) (locationahead 'dumpy ?ahead ?time) (wall ?ahead) (previously ?time ?previous-time) (not (action 'right ?previous-time)) (not (action 'shoot ?previous-time)) (left-of 'dumpy ?location-left ?time) (not (wall ?location-left)) (not (visited ?location-left)))) (<- (medium 'left ?time) (and (at 'dumpy ?location ?time) (locationahead 'dumpy ?ahead ?time) (wall ?ahead) (previously ?time ?previous-time) (not (action 'right ?previous-time)) (not (action 'shoot ?previous-time)) (left-of 'dumpy ?location-left ?time) (not (wall ?location-left)) (visited ?location-left))) (<- (good 'left ?time) (and (at 'dumpy ?location ?time) (locationahead 'dumpy ?ahead ?time) (visited ?ahead) (previously ?time ?previous-time) (not (action 'right ?previous-time)) (not (action 'shoot ?previous-time)) (left-of 'dumpy ?location-left ?time) (not (wall ?location-left)) (not (visited ?location-left)) (ok ?location-left))) (<- (medium 'right ?time) (and (at 'dumpy ?location ?time) (locationahead 'dumpy ?ahead ?time) (not (ok ?ahead)) (previously ?time ?previous-time) (not (action 'left ?previous-time)) (not (action 'shoot ?previous-time)) (right-of 'dumpy ?location-right ?time) (not (wall ?location-right)))) (<- (good 'right ?time) (and (at 'dumpy ?location ?time) (locationahead 'dumpy ?ahead ?time) (wall ?ahead) (previously ?time ?previous-time) (not (action 'left ?previous-time)) (not (action 'shoot ?previous-time)) (right-of 'dumpy ?location-right ?time) (not (wall ?location-right)) (not (visited ?location-right)))) (<- (medium 'right ?time) (and (at 'dumpy ?location ?time) (locationahead 'dumpy ?ahead ?time) (wall ?ahead) (previously ?time ?previous-time) (not (action 'left ?previous-time)) (not (action 'shoot ?previous-time)) (right-of 'dumpy ?location-right ?time) (not (wall ?location-right)) (visited ?location-right))) (<- (good 'right ?time) (and (at 'dumpy ?location ?time) (locationahead 'dumpy ?ahead ?time) (visited ?ahead) (previously ?time ?previous-time) (not (action 'left ?previous-time)) (not (action 'shoot ?previous-time)) (right-of 'dumpy ?location-right ?time) (not (wall ?location-right)) (not (visited ?location-right)) (ok ?location-right))) (<- (good 'give-up ?time) (and (at 'dumpy ?location ?time) (risky 'forward ?time) (previously-visited 'dumpy ?prev ?pt ?time) (entered ?prev ?pt ?ent) (second-visit ?prev ?ent))) A.10 abstractions.cl (defun x-coord (pos) "Plukker ut x koordinaten" (first pos)) (defun y-coord (pos) "Plukker ut y-kooordinaten" (second pos)) (defun stench (percepts) "Plukker ut stench-persepsjonen" (svref percepts 0)) (defun (setf stench) (val vec) "Setter persepsjonen stench paa riktig plass i vektoren" (setf (svref vec 0) val)) (defun perceived-stench (percepts) "Tester om persepsjon er stench" (eq (stench percepts) 'stench)) (defun breeze (percepts) "Plukker ut breeze-persepsjonen" (svref percepts 1)) (defun (setf breeze) (val vec) "Setter persepsjonen breeze paa riktig plass i vektoren" (setf (svref vec 1) val)) (defun perceived-breeze (percepts) "Tester om persepsjon er breeze" (eq (breeze percepts) 'breeze)) (defun glitter (percepts) "Plukker ut glitter-persepsjonen" (svref percepts 2)) (defun (setf glitter) (val vec) "Setter persepsjonen glitter paa riktig plass i vektoren" (setf (svref vec 2) val)) (defun perceived-glitter (percepts) "Tester om persepsjon er glitter" (eq (glitter percepts) 'glitter)) (defun bump (percepts) "Plukker ut bump-persepsjonen" (svref percepts 3)) (defun (setf bump) (val vec) "Setter persepsjonen bump paa riktig plass i vektoren" (setf (svref vec 3) val)) (defun perceived-bump (percepts) "Tester om persepsjon er bump" (eq (bump percepts) 'bump)) (defun scream (percepts) "Plukker ut screem-persepsjonen" (svref percepts 4)) (defun (setf scream) (val vec) "Setter persepsjonen screem paa riktig plass i vektoren" (setf (svref vec 4) val)) (defun perceived-scream (percepts) "Tester om persepsjon er screem" (eq (scream percepts) 'scream)) (defun find-coordinates-of-player () (find-in-array 'player 1 1 *row-length*)) (defun map-to-cartesian (i s) "returns the cartesian cooridinates by rotate the coord-system 90 degrees" (let ((x s) (y (- (+ 1 *row-length*) i))) (list x y))) (defun map-from-cartesian (x y) "returns the coordinates according to Common Lisp arrays, rotating the cartesian 270 degrees" (let ((i (- (+ 1 *row-length*) y)) (s x)) (list i s))) A.11 utilities.cl (defun find-type (type list) (find-if (lambda (x) (typep x type)) list)) (defun find-in-vec (x i s max) (cond ((> s max) nil) ((find-type x (contents (aref *board* i s))) (list i s)) (t (find-in-vec x i (+ s 1) max)))) (defun find-in-array (x i s max) (cond ((> i max) nil) ((find-in-vec x i s max)) (t (find-in-array x (+ i 1) s max)))) (defun next-coord (dir i s) (case dir (north (list (- i 1) s)) (south (list (+ i 1) s)) (west (list i (- s 1))) (east (list i (+ s 1))) (t "feil retningsangivelse til next-coord; foelg himmelretningen"))) (defun next-square (dir i s) (let ((new-coord (next-coord dir i s))) (aref *board* (first new-coord) (second new-coord)))) (defun neighbours (i s) (list (next-coord 'west i s) (next-coord 'north i s) (next-coord 'east i s) (next-coord 'south i s))) (defun mark-neighbours (board i s predicate) (dolist (obj (neighbours i s)) (let ((square (aref board (x-coord obj) (y-coord obj)))) (when (and (not (typep square 'wall)) (not (member predicate (predicates square)))) (setf (predicates square) (cons predicate (predicates square))))))) (defun probably () (= 0 (random 5))) (defun random-square (n) (list (+ 1 (random n)) (+ 1 (random n)))) (defun place-randomly (n) (let ((square (random-square n))) (if (equal square *start*) (place-randomly n) square))) (defun fill-randomly (array) (let* ((dim (array-dimensions array)) (ver (- (first dim) 2)) (hor (- (second dim) 2))) (do ((i 1 (+ i 1))) ((= i ver)) (dotimes (j ver array) (when (and (probably) (not (and (= i (x-coord *start*)) (= (+ j 1) (y-coord *start*))))) (setf (aref array i (+ j 1)) (make-instance 'pit)) (mark-neighbours array i (+ j 1) 'breeze)))))) (defun initialize-perceptions (agent) "Setter tre av persepsjonene til objektet _agent_ til _none_ " (setf (scream (percepts agent)) 'none (stench (percepts agent)) 'none (breeze (percepts agent)) 'none (glitter (percepts agent)) 'none)) (defmethod update-agent ((ag player) (sq open-square)) "Oppdaterer agenten hvis den skulle paa noen maate doe. Setter agentens persepsjoner til none (initialize-perceptions arg) for derefter aa sette agentens persepsjonsvektor til aa inneholde de predikatene som eksisterer paa ruten." (let ((wumpus (find-type 'wumpus (contents sq)))) (cond ((or (typep sq 'pit) (and wumpus (alive wumpus))) (setf (alive ag) nil)) (t (initialize-perceptions ag) (let ((predicates (predicates sq))) (loop for predicate in predicates do (case predicate (scream (setf (scream (percepts ag)) 'scream)) (stench (setf (stench (percepts ag)) 'stench)) (breeze (setf (breeze (percepts ag)) 'breeze)) (glitter (setf (glitter (percepts ag)) 'glitter))))))))) (defun list-perceptions (percept-vector) (loop for elm across percept-vector when (not (equal elm 'none)) collect elm into perceptions finally (return (if (null perceptions) '(none) perceptions)))) (defun display-universal-time () (multiple-value-bind (sec min hour day mth year) (decode-universal-time (get-universal-time)) (format t " ~a:~a:~a (~a-~a-~a) "hour min sec year mth day))) A.12 init.cl (defun initboard (n) (setf dumpy (make-instance 'player :ag-name "Dumpy")) (setf wumpus (make-instance 'wumpus :ag-name "Wumpus")) (let* ((board (make-array `(,n ,n) :initial-element nil)) (board-len (- n 2)) (wump-square (place-randomly board-len)) (gold-square (place-randomly board-len))) (do ((i 0 (+ i 1))) ((= i n)) (setf (aref board 0 i) (make-instance 'wall) (aref (aref (aref (do board i 0) (make-instance 'wall) board (- n 1) i) (make-instance 'wall) board i (- n 1)) (make-instance 'wall))) ((i 1 (+ i 1))) ((> i board-len)) (dotimes (j board-len) (setf (aref board i (+ j 1)) (make-instance 'open-square)))) (fill-randomly board) (setf (contents (aref board (first wump-square) (second wump-square))) (cons wumpus (contents (aref board (first wump-square) (second wump-square)))) (contents (aref board (first gold-square) (second gold-square))) (cons (make-instance 'gold) (contents (aref board (first gold-square) (second gold-square)))) (predicates (aref board (first gold-square)(second gold-square))) (cons 'glitter (predicates (aref board (first gold-square) (second gold-square)))) (contents (aref board (first *start*) (second *start*))) (cons dumpy (contents (aref board (first *start*) (second *start*))))) (mark-neighbours board (first wump-square) (second wump-square) 'stench) (update-agent dumpy (aref board (x-coord *start*) (y-coord *start*))) board)) A.13 pretty-printing.cl ;;; -------------------------- utskriftsprosedyrer ---------------------------(defun skriv-blanke (n) (dotimes (i n) (format t "~a" #\space))) (defun midtstill-ord (ord tot) (let* ((len (length ord)) (rest (- tot len))) (if (> (mod rest 2) 0) (let ((before (floor (/ rest 2)))) (format t "|") (skriv-blanke before) (format t "~a" ord) (skriv-blanke (+ before 1))) (let ((before (/ rest 2))) (format t "|") (skriv-blanke before) (format t "~a" ord) (skriv-blanke before))))) (defun blank-square () (format t "| ~9,A" #\space)) (defun print-board () (let ((contents nil) (predicates nil)) (print-line) (let ((len (array-dimension *board* 0))) (do ((i 0 (+ i 1))) ((= i len)) (format t "~1%") (dotimes (j len) (let ((square (aref *board* i j))) (typecase square (wall (midtstill-ord "wall" 10)) (square (progn (if (typep square 'pit) (midtstill-ord "pit" 10) (blank-square)) (when (predicates square) (setf predicates (append predicates (make-alist (predicates square) j)))) (when (contents square) (setf contents (append contents (make-alist (contents square) j))))))))) (terpri) (when (not (null contents)) (midtstill-ord "wall" 10) (print-restlist contents) (setf contents nil)) (when (not (null predicates)) (midtstill-ord "wall" 10) (print-restlist predicates) (setf predicates nil)) (print-line))))) (defun plasser-ord (ord n) (dotimes (i (- n 1)) (blank-square)) (midtstill-ord ord 10)) (defun print-rest (a-list) (print-r 0 a-list)) (defun print-r (s-pos a-list) (if (null a-list) (skriv-n-blanke (- s-pos 1)) (let* ((start (first a-list)) (word (car start)) (n-pos (cdr start))) (plasser-ord (lag-streng word) (- n-pos s-pos)) (print-r n-pos (cdr a-list))))) (defun lag-streng (obj) (typecase obj (symbol (string-downcase (string obj))) (t (let ((name (name obj))) (cond ((equal name "Dumpy") (let ((orientation (facing dumpy))) (case orientation (north (concatenate 'string "Dumpy " "'|`")) (south (concatenate 'string "Dumpy " " \|/")) (west (concatenate 'string "Dumpy " "<-")) (east (concatenate 'string "Dumpy " "->"))))) ((equal name "Wumpus") (if (alive wumpus) name (concatenate 'string "Wumpus " "+"))) (t name)))))) (defun skriv-n-blanke (n) (dotimes (i (- *row-length* n)) (blank-square)) (terpri)) (defun print-line () (format t "~a" (make-string 70 :initial-element #\_))) (defun print-restlist (a-list) (let ((new-list (count-and-divide a-list))) (defun print-list (list) (if (null list) nil (progn (print-rest (car list)) (if (not (null (cdr list))) (midtstill-ord "wall" 10)) (print-list (cdr list))))) (print-list new-list))) (defun make-alist (list n) (mapcar (lambda (x) (cons x n)) list)) (defun count-and-divide (a-list) (if (null a-list) nil (let* ((forrige (first a-list)) (pos (cdr forrige)) (new-list nil)) (defun divide (old-pos list) (let* ((start (first list)) (new-pos (cdr start))) (cond ((null list) (reverse new-list)) ((eql new-pos old-pos) (setf a-list (remove start a-list)) (push start new-list) (divide new-pos (cdr list))) (t (divide new-pos (cdr list)))))) (let ((divided-list (divide pos (cdr a-list)))) (if (null divided-list) (list a-list) (cons a-list (count-and-divide divided-list))))))) A.14 gold-digger.cl (defun gold-digger (dumpy) (let ((files (directory #p"D:/Private/SLI-370/Wumpus/Go/wumpus-1999-10-20/Go/*.cl*"))) (loop for file in files do (load file)) (setf *test* (initboard 6)) (setf *board* *test*) (print-board) (when (alive dumpy) (loop (format t " ~& WUMPUS > ") (let ((action (read))) (case action (go-forward (implement-action dumpy action) (if (alive dumpy) (print-board) (return-from nil))) ((turn-left turn-right) (implement-action dumpy action) (format t " Your agent is heading ~A ~% " (facing dumpy))) (grab (if (grab dumpy 'gold) (format t " Your agent is in possesion of ~A ~%" (possesions dumpy)) (format t " Sorry Mac, no need for goldfever here!~%"))) (shoot (shoot dumpy) (if (alive Wumpy) (format t " Your agent is now in possesion of ~A ~%" (possesions dumpy)) (format t " Your agent got the big Wumpy! ~%"))) (heading? (format t " Your agent is heading ~A ~% " (facing dumpy))) (possesions? (if (and (alive Wumpy) (eq nil (possesions dumpy))) (format t " Your agent is now in possesion of ~A, ~% and yet the big Wumpy isn't dead! ~%" (possesions dumpy)) (format t " Your agent is now in possesion of ~A .~%" (possesions dumpy)))) (show-board (print-board)) (alive? (if (alive dumpy) (format t " Hey kid! Cheer up!~%") (format t " Argh, you blew it, cowboy!~%"))) (perceptions? (format t " Your agent percives ~A~% " (percepts dumpy))) (help (format t " Valide kommandoer er go-forward, turn-left, turn-right,~% shoot, grab, heading?,~% perceptions?, possesions?, show-board~% help, and q. ~%")) (q (return-from nil)) (otherwise (format t " What!? No command named ~A. ~& WUMPUS >" action)))))))) A.15 prolog-till.cl ;; noen hjelpefunskjoner til Prolog, hentet fra eller lagd ut i fra ideer hos Norvig, ;; Paradigms of Artificial Intelligence (defvar *db-predicates* nil "a list of all predicates stored in the database.") (defun get-clauses (pred) (get pred 'clauses)) ;Norvig (defun predic (relation) (first relation)) ;Norvig (defun clause-head (clause) (first clause)) (defun clause-body (clause) (rest clause)) ;Norvig ;Norvig (defun clear-rules () (setf *rules* nil)) (defun clear-db () (dolist (pred *db-predicates*) (clear-predicate pred)) (setf *db-predicates* nil) (clear-rules)) (defun clear-predicate (predicate) "remove the clauses for a single predicate." (setf (get predicate 'clauses) nil)) ;Norvig (defun remove-predicates (predicate) "fjerner funksjonene som tilhrer et predikat fra *rules*" (dolist (pred (get predicate 'clauses)) (setf *rules* (delete pred *rules* :test #'equal)))) (defun clear-predicates (predicate) ;OBS vr klar over at denne ogs fjerner fakta som begynner p pred, ;ikke bare regler (remove-predicates predicate) (clear-predicate predicate)) (defmacro <- (con &rest ant) (let ((ant (if (= (length ant) 1) (car ant) `(and ,@ant))) (pred (predic con))) `(let ((rules ,(rule-fn (rep_ ant) (rep_ con)))) (conc1f *db-predicates* ',pred) (conc1f (get ',pred 'clauses) rules) (length (conc1f *rules* rules))))) (defun make-action-query (time) (car (or (ask-pattern `(great ?a ,time)) (ask-pattern `(good ?a ,time)) (ask-pattern `(medium ?a ,time)) (ask-pattern `(risky ?a ,time))))) (defun make-location-query (time) (car (ask-pattern `(at 'dumpy ?l ,time)))) (defun make-orientation-query (time) (car (ask-pattern `(orientation 'dumpy ?l ,time)))) (defun make-action-sentence (action time) `(action ,action ,time)) (defun make-percept-sentence-prol (percepts time) (let ((percept-list (list `',(stench percepts) `',(breeze percepts) `',(glitter percepts) `',(bump percepts) `',(scream percepts)))) `(percept ,percept-list ,time))) (defun random-elt (list) (let* ((len (length list)) (number (random len))) (nth number list))) (defun ask-pattern (query) (let ((noe (bag-of query))) (if noe (random-elt noe) nil))) (defun tell (percepts) (eval `(<- ,percepts))) (defun tell-first (fact) (eval `(push-rule ,fact (cut)))) (defmacro push-rule (con &rest ant) (let ((ant (if (= (length ant) 1) (car ant) `(and ,@ant))) (pred (predic con))) `(let ((rules ,(rule-fn (rep_ ant) (rep_ con)))) (conc2f *db-predicates* ',pred) (conc2f (get ',pred 'clauses) rules) (length (conc2f *rules* rules))))) (define-modify-macro conc2f (obj) ;legger til obj foran (lambda (place obj) (nconc (list obj) place))) A.16 onprolog3.lisp ;;; Sources from Paul Graham's _On Lisp: Advanced Techniques for ;;; Common Lisp_, Prentice Hall, 1994. ; ; ; ; ; ; ; ; ; ; ; The code in this file was mechanically extracted from the TeX source files of On Lisp. Some operators are multiply defined, as they were in the book. Usually this means just that you get an upwardly compatible version 2 of whatever it is. Note, though, that if you load this whole file you get: 1. the cltl1 versions of alrec and atrec. 2. varsym? defined as needed by the Prolog compiler. So if you want to use e.g. match with variables that begin with question marks, comment out the final definition of varsym? If you have questions or comments about this code, or you want something I didn't include, send mail to onlisp@das.harvard.edu. ;; krever prolog-till (defmacro aif (test-form then-form &optional else-form) `(let ((it ,test-form)) (if it ,then-form ,else-form))) (defmacro aif2 (test &optional then else) (let ((win (gensym))) `(multiple-value-bind (it ,win) ,test (if (or it ,win) ,then ,else)))) (defmacro acond2 (&rest clauses) (if (null clauses) nil (let ((cl1 (car clauses)) (val (gensym)) (win (gensym))) `(multiple-value-bind (,val ,win) ,(car cl1) (if (or ,val ,win) (let ((it ,val)) ,@(cdr cl1)) (acond2 ,@(cdr clauses))))))) (define-modify-macro conc1f (obj) (lambda (place obj) (nconc place (list obj)))) (defmacro with-gensyms (syms &body body) `(let ,(mapcar #'(lambda (s) `(,s (gensym))) syms) ,@body)) (defun match (x y &optional binds) (acond2 ((or (eql x y) (eql x '_) (eql y '_)) (values binds t)) ((binding x binds) (match it y binds)) ((binding y binds) (match x it binds)) ((varsym? x) (values (cons (cons x y) binds) t)) ((varsym? y) (values (cons (cons y x) binds) t)) ((and (consp x) (consp y) (match (car x) (car y) binds)) (match (cdr x) (cdr y) it)) (t (values nil nil)))) (defun binding (x binds) (labels ((recbind (x binds) (aif (assoc x binds) (or (recbind (cdr it) binds) it)))) (let ((b (recbind x binds))) (values (cdr b) b)))) (defun vars-in (expr &optional (atom? #'atom)) (if (funcall atom? expr) (if (var? expr) (list expr)) (union (vars-in (car expr) atom?) (vars-in (cdr expr) atom?)))) (defun var? (x) (and (symbolp x) (eq (char (symbol-name x) 0) #\?))) (defun simple? (x) (or (atom x) (eq (car x) 'quote))) (setq *cont* #'identity) (defmacro =lambda (parms &body body) `#'(lambda (*cont* ,@parms) ,@body)) (defmacro =defun (name parms &body body) (let ((f (intern (concatenate 'string "=" (symbol-name name))))) `(progn (defmacro ,name ,parms `(,',f *cont* ,,@parms)) (defun ,f (*cont* ,@parms) ,@body)))) (defmacro =bind (parms expr &body body) `(let ((*cont* #'(lambda ,parms ,@body))) ,expr)) (defmacro =values (&rest retvals) `(funcall *cont* ,@retvals)) (defmacro =funcall (fn &rest args) `(funcall ,fn *cont* ,@args)) (defmacro =apply (fn &rest args) `(apply ,fn *cont* ,@args)) (defparameter *paths* nil) (defconstant failsym '@) (defmacro choose (&rest choices) (if choices `(progn ,@(mapcar #'(lambda (c) `(push #'(lambda () ,c) *paths*)) (reverse (cdr choices))) ,(car choices)) '(fail))) (defmacro choose-bind (var choices &body body) `(cb #'(lambda (,var) ,@body) ,choices)) (defun cb (fn choices) (if choices (progn (if (cdr choices) (push #'(lambda () (cb fn (cdr choices))) *paths*)) (funcall fn (car choices))) (fail))) (defun fail () (if *paths* (funcall (pop *paths*)) failsym)) (defun rep_ (x) (if (atom x) (if (eq x '_) (gensym "?") x) (cons (rep_ (car x)) (rep_ (cdr x))))) (defun fullbind (x b) (cond ((varsym? x) (aif2 (binding x b) (fullbind it b) (gensym))) ((atom x) x) (t (cons (fullbind (car x) b) (fullbind (cdr x) b))))) (defun varsym? (x) (and (symbolp x) (not (symbol-package x)))) (defun form (pat) (if (simple? pat) pat `(cons ,(form (car pat)) ,(form (cdr pat))))) (defvar *rules* nil) ;;;(defmacro <- (con &rest ant) ;;; ;;; (let ((ant (if (= (length ant) 1) ;;; ;;; (car ant) ;;; ;;; `(and ,@ant)))) ;;; ;;; `(length (conc1f *rules* ;;; ;;; ,(rule-fn (rep_ ant) (rep_ con)))))) (defun rule-fn (ant con) (with-gensyms (val win fact binds paths) `(=lambda (,fact ,binds ,paths) (with-gensyms ,(vars-in (list ant con) #'simple?) (multiple-value-bind (,val ,win) (match ,fact (list ',(car con) ,@(mapcar #'form (cdr con))) ,binds) (if ,win ,(gen-query ant val paths) (fail))))))) ; ; ;;;(defmacro with-inference (query &rest body) ;;; ;;; (let ((vars (vars-in query #'simple?)) (gb (gensym))) ;;; ;;; `(with-gensyms ,vars ;;; ;;; (setq *paths* nil) ;;; ;;; (=bind (,gb) ,(gen-query (rep_ query) nil '*paths*) ; ;;; ;;; (let ,(mapcar #'(lambda (v) ;;; ;;; `(,v (fullbind ,v ,gb))) ;;; ;;; ;;; ;;; ;;; ;;; vars) ,@body) (fail))))) (defun gen-query (expr binds paths) (case (car expr) (and (gen-and (cdr expr) binds paths)) (or (gen-or (cdr expr) binds paths)) (not (gen-not (cadr expr) binds paths)) (lisp (gen-lisp (cadr expr) binds)) (is (gen-is (cadr expr) (third expr) binds)) (cut `(progn (setq *paths* ,paths) (=values ,binds))) (t `(prove (list ',(car expr) ,@(mapcar #'form (cdr expr))) ,binds *paths*)))) ; (=defun prove (query binds paths) (choose-bind r *rules* (=funcall r query binds paths))) ; ; ; ; ; ; ; ; ; ; (defun gen-and (clauses binds paths) ; (if (null clauses) `(=values ,binds) (let ((gb (gensym))) `(=bind (,gb) ,(gen-query (car clauses) binds paths); ,(gen-and (cdr clauses) gb paths))))) ; (defun gen-or (clauses binds paths) `(choose ,@(mapcar #'(lambda (c) (gen-query c binds paths)) clauses))) ; (defun gen-not (expr binds paths) (let ((gpaths (gensym))) `(let ((,gpaths *paths*)) (setq *paths* nil) (choose (=bind (b) ,(gen-query expr binds paths) (setq *paths* ,gpaths) (fail)) (progn (setq *paths* ,gpaths) ; ; ; (=values ,binds)))))) (defmacro with-binds (binds expr) `(let ,(mapcar #'(lambda (v) `(,v (fullbind ,v ,binds))) (vars-in expr)) ,expr)) (defun gen-lisp (expr binds) `(if (with-binds ,binds ,expr) (=values ,binds) (fail))) (defun gen-is (expr1 expr2 binds) `(aif2 (match ,expr1 (with-binds ,binds ,expr2) ,binds) (=values it) (fail))) ; This code is copyright 1993 by Paul Graham, but anyone who wants ; to use the code in any nonprofit activity, or distribute free ; verbatim copies (including this notice), is encouraged to do so. Tillegg B Kjringseksempel > (w) ; Fast loading A:\Wumpus-Windows\Wumpus-system\action-value.fasl ; Fast loading A:\Wumpus-Windows\Wumpus-system\wumpus-rules.fasl [Live data follows..] ______________________________________________________________________ | wall | wall | wall | wall | wall | wall ______________________________________________________________________ | wall | pit | | pit | pit | wall | wall | | breeze | breeze | | ______________________________________________________________________ | wall | | | | | wall | wall | breeze | | breeze | breeze | ______________________________________________________________________ | wall | | | pit | | wall | wall | | breeze | stench | breeze | ______________________________________________________________________ | wall | | | | | wall | wall | Dumpy -> | gold | Wumpus | | | wall | | stench | breeze | stench | | wall | | glitter | | | ______________________________________________________________________ | wall | wall | wall | wall | wall | wall ______________________________________________________________________ My perceptions at time 21:31:36 (1999-11-28) are/is none, and the number in my action sequence is 0. I'm facing east. (Your agent is walking forward.) 67 ______________________________________________________________________ | wall | wall | wall | wall | wall | wall ______________________________________________________________________ | wall | pit | | pit | pit | wall | wall | | breeze | breeze | | ______________________________________________________________________ | wall | | | | | wall | wall | breeze | | breeze | breeze | ______________________________________________________________________ | wall | | | pit | | wall | wall | | breeze | stench | breeze | ______________________________________________________________________ | wall | | | | | wall | wall | | Dumpy -> | Wumpus | | | wall | | gold | | | | wall | | stench | breeze | stench | | wall | | glitter | | | ______________________________________________________________________ | wall | wall | wall | wall | wall | wall ______________________________________________________________________ My perceptions at time 21:31:43 (1999-11-28) are/is stench, glitter, and the number in my action sequence is 1. I'm facing east. She is grabbing the gold.. Yes! Your agent has it, and is changing strategy, to go home! ______________________________________________________________________ | wall | wall | wall | wall | wall | wall ______________________________________________________________________ | wall | pit | | pit | pit | wall | wall | | breeze | breeze | | ______________________________________________________________________ | wall | | | | | wall | wall | breeze | | breeze | breeze | ______________________________________________________________________ | wall | | | pit | | wall | wall | | breeze | stench | breeze | ______________________________________________________________________ | wall | | | | | wall | wall | | Dumpy -> | Wumpus | | | wall | | stench | breeze | stench | ______________________________________________________________________ | wall | wall | wall | wall | wall | wall ______________________________________________________________________ ; Fast loading A:\Wumpus-Windows\Wumpus-system\new-values.fasl My perceptions at time 21:32:21 (1999-11-28) are/is stench, glitter, and the number in my action sequence is 2. I'm facing east. (Your agent is turning right.) ______________________________________________________________________ | wall | wall | wall | wall | wall | wall ______________________________________________________________________ | wall | pit | | pit | pit | wall | wall | | breeze | breeze | | ______________________________________________________________________ | wall | | | | | wall | wall | breeze | | breeze | breeze | ______________________________________________________________________ | wall | | | pit | | wall | wall | | breeze | stench | breeze | ______________________________________________________________________ | wall | | | | | wall | wall | |Dumpy |/ | Wumpus | | | wall | | stench | breeze | stench | ______________________________________________________________________ | wall | wall | wall | wall | wall | wall ______________________________________________________________________ My perceptions at time 21:32:23 (1999-11-28) are/is stench, glitter, and the number in my action sequence is 3. I'm facing south. (Your agent is turning right.) ______________________________________________________________________ | wall | wall | wall | wall | wall | wall ______________________________________________________________________ | wall | pit | | pit | pit | wall | wall | | breeze | breeze | | ______________________________________________________________________ | wall | | | | | wall | wall | breeze | | breeze | breeze | ______________________________________________________________________ | wall | | | pit | | wall | wall | | breeze | stench | breeze | ______________________________________________________________________ | wall | | | | | wall | wall | | Dumpy <- | Wumpus | | | wall | | stench | breeze | stench | ______________________________________________________________________ | wall | wall | wall | wall | wall | wall ______________________________________________________________________ My perceptions at time 21:32:24 (1999-11-28) are/is stench, glitter, and the number in my action sequence is 4. I'm facing west. (Your agent is walking forward.) ______________________________________________________________________ | wall | wall | wall | wall | wall | wall ______________________________________________________________________ | wall | pit | | pit | pit | wall | wall | | breeze | breeze | | ______________________________________________________________________ | wall | | | | | wall | wall | breeze | | breeze | breeze | ______________________________________________________________________ | wall | | | pit | | wall | wall | | breeze | stench | breeze | ______________________________________________________________________ | wall | | | | | wall | wall | Dumpy <- | | Wumpus | | | wall | | stench | breeze | stench | ______________________________________________________________________ | wall | wall | wall | wall | wall | wall ______________________________________________________________________ My perceptions at time 21:33:44 (1999-11-28) are/is none, and the number in my action sequence is 5. I'm facing west. (She is climbing out of this world..) Congratulation! Your agent has now reached her goal. In the new version of the game, you can climb to the next level. Until then, BYE! ______________________________________________________________________ | wall | wall | wall | wall | wall | wall ______________________________________________________________________ | wall | pit | | pit | pit | wall | wall | | breeze | breeze | | ______________________________________________________________________ | wall | | | | | wall | wall | breeze | | breeze | breeze | ______________________________________________________________________ | wall | | | pit | | wall | wall | | breeze | stench | breeze | ______________________________________________________________________ | wall | | | | | wall | wall | | | Wumpus | | | wall | | stench | breeze | stench | ______________________________________________________________________ | wall | wall | wall | wall | wall | wall ______________________________________________________________________ 995 > Bibliogra Bratko, I. (1990). Prolog - Programming for Articial Intelligence. Addison-Wesley Publishers. Clocksin, W. F. og Mellish, C. S. (1987). Programming in Prolog. Springer-Verlag. Graham, P. (1994). On Lisp, Advanced Techniques for Common Lisp. Prentice-Hall. Norvig, P. (1992). Paradigms of Articial Intelligence Programming. Morgan kaufmann Publishers. Russel, S. og Norvig, P. (1995). Articial Intelligence, A Modern Approach. Prentice Hall. 73