====== Slagskip (Battleship) ====== I denne oppskriften skal du bygge opp det klassiske spillet "Slagskip" trinn for trinn. Dette er et spill for to personer, og hver spiller trenger en micro:bit, altså trengs det to micro:biter til sammen. Dersom du ikke har noen å spille med kan du spille mot deg selv i forhåndsvisningen til makecode, da trenger du ikke fysiske micro:bit i det hele tatt. Dette spillet er et såkalt symmetrisk spill hvor alt er likt for begge spillerne, så det er tilstrekkelig å lage ett enkelt program og lagre dette programmet på to micro:biter. ===== Sette opp radio-kommunikasjon mellom de to micro:bitene ===== For at flere micro:biter skal kunne kommunisere med hverandre via radio, må de tilhøre samme radiogruppe. Litt forenklet kan du si at micro:biter med samme radiogruppe kan kommunisere med hverandre, og micro:biter med ulik radiogruppe ikke kan kommunisere med hverandre. Dersom dere er flere i samme rom som bruker micro:bit bør dere velge hver deres radiogruppe. Ved start skal du sette din micro:bit til en eller annen radiogruppe, f.eks. 1. Du kan velge hvilket som helst tall mellom 0 og 255. Du finner kommandoen for å sette radiogruppe i menyen for **Radio**. {{url>https://jsfiddle.net/royeven/b57f9nke/1/embedded/css,html,result/ 100%,400 noscroll noborder left}} ===== Lage flyttbar brikke (sprite) ===== En figur i et spill kalles ofte en sprite, og på norsk kan vi oversette det med brikke. Nå skal du lage en brikke som du kan flytte rundt på skjermen ved å bruke knappene A og B. Når knapp A trykkes skal brikken flytte seg bortover og når knapp B trykkes skal brikken flyttes nedover. Når brikken kommer til kanten av brettet må du selv bestemme hvordan du vil få den til å skifte retning. Du trenger en variabel å lagre brikken i. Denne variabelen kan du kalle //sprite//. Du finner alle kommandoer du trenger til dette i menyen **Variabler**. Du finner funksjonen for å lage en brikke i menyen for **Spill**. Der finnes det en rund blokk som lager en brikke på en bestemt X- og Y-koordinat. Det er mulig du først må trykke på **Avansert** for å finne **Spill**-menyen. Koordinatene til brikken skal være mellom 0 og 4. X-retningen er "bortover" og Y-retningen er "nedover". Koordinat X=0, Y=0 betyr 0 bortover og 0 nedover, altså øverst i venstre hjørne. Koordinat X=4,Y=0 betyr 4 bortover, 0 nedover, altså øverst i høyre hjørne. Du kan gjerne bruke funksjonen for "tilfeldig tall" i menyen for **Matematikk** til å legge brikken på en tilfeldig plass mellom 0 og 4 på skjermen. For å flytte brikken ved å trykke på knappene trenger du blokkene "Når knapp A trykkes" og "Når knapp B trykkes" fra **Inndata**-menyen. I menyen for **Spill** finner du kommandoer for å angi brikkens posisjon til noe (altså flytte den til en bestemt posisjon) eller å endre brikkens posisjon i X- eller Y-retning. I løsningsforslaget er det vist to forskjellige måter å flytte brikken på, og det finnes sikkert enda flere måter å gjøre det på. Du kan selv velge hvilken måte du foretrekker. Så lenge du kan styre //sprite// dit du vil har du gjort det riktig. {{url>https://jsfiddle.net/royeven/b57f9nke/5/embedded/css,html,result/ 100%,400 noscroll noborder left}} ===== Lage variabler for skipene ===== Nå skal du opprette brikker for dine skip. Spillerne blir enige om hvor mange skip man skal benytte. Av og til ønsker man få skip og av og til ønsker man mange skip. Du skal lage en variabel som du kaller //antall_skip//. Du kan selv bestemme verdien på denne variabelen, men det kan være lurt å begrense antallet skip til et sted mellom 1 og 3. Du skal også lage en variabel til å lagre alle skipene i. Denne skal du kalle //mine_skip//, og inntil videre skal du ikke gjøre noe med denne. Sjekk at variabelen //mine_skip// finnes når du går inn i menyen for **Variabler**. Hvis den ikke finnes må du lage den. {{url>https://jsfiddle.net/royeven/b57f9nke/23/embedded/css,html,result/ 100%,400 noscroll noborder left}} ===== Lage brikker for skipene ===== I Slagskip er det vanligvis spilleren som selv plasserer skipene der man vil. Ettersom du spiller på en datamaskin (micro:bit) kan datamaskinen plassere skipene på tilfeldige plasser for deg. Utplassering av skip skal bare gjøres én gang, og dette er gunstigst å gjøre på starten. Du bør bruke en "gjenta"-løkke (se i menyen under **Løkker**) til å lage like mange skip som du bestemte i forrige trinn. Det vil være en fordel for deg om du får brikkene som skal representere skip til å se annerledes ut enn den flyttbare brikken som du lagde tidligere. F.eks. kan du angi en annen lysstyrke eller angi en blinking på skipenes brikker ved å bruke angi-kommandoen fra Spill. Ettersom antallet skip kan variere kan vi ikke lagre skipene på samme måte som vi lagrer variabelen //sprite//. //sprite// skal det bare finnes én av, men antall skip kan variere, og vi kan ikke ha et variabelt antall variabler. Derfor skal vi bruke den ene variabelen //mine_skip// som en tabell, og i denne ene tabellen kan du lagre så mange skip du vil. Spillet blir mer spennende dersom skipene blir plassert på tilfeldige steder. Du kan legge til et skip i tabellen //mine_skip// ved å bruke kommandoen for "//mine_skip// legg til verdi til slutt". Denne finner du i menyen **Tabell** under **Avansert**. Ved å kombinere variabelen //antall_skip// med en "Gjenta N ganger"-løkke kan du få "Gjenta //antall_skip// ganger". Gjenta følgende oppskrift antall_skip ganger: - Lag en ny variabel kalt //nytt_skip// og sett den til en ny brikke på en tilfeldig X-koordinat og en tilfeldig Y-koordinat. - Angi at //nytt_skip// skal blinke i takt 1. Du finner angi-kommandoen under **Spill**-menyen - Legg til verdien //nytt_skip// til slutt i tabellen //mine_skip// {{url>https://jsfiddle.net/royeven/b57f9nke/9/embedded/css,html,result/ 100%,400 noscroll noborder left}} ===== Liv og poeng ===== I menyen for **Spill** finner du kommandoer for å sette poeng og sette liv. Ved oppstart av spillet skal du ha 0 poeng og like mange liv som du har skip. Hver gang du senker et fiendeskip skal du få ett poeng, og når du mister ett av dine egne skip skal du miste ett liv. Poengsummen forteller hvor mange fiendeskip du har senket og livene er dine gjenværende skip. {{url>https://jsfiddle.net/royeven/b57f9nke/10/embedded/css,html,result/ 100%,400 noscroll noborder left}} ===== Bombe den andre spillerens skip ===== Ved å bruke knappene A og B er du nå i stand til å flytte brikken kalt sprite fram og tilbake og opp og ned på skjermen. Når du trykker på knappene A og B samtidig (altså A+B) skal du sende X- og Y-koordinaten du bomber til fiendens micro:bit. Du trenger "Når knapp A+B trykkes"-blokka fra inndata, og du trenger radio-send-tekst-kommandoen fra **Radio**-menyen. Du skal sende både en X- og en Y-koordinat for stedet du bomber. Teksten som du skal sende til fienden må altså være sammensatt av minst to verdier, og du kan bruke kommandoen "sett sammen" fra **Tekst**-menyen til å sette sammen flere ledd til én tekst. I tillegg trenger du "Når knapp A+B trykkes"-blokka fra **Inndata**. Du kan finne sprite sin X- og Y-koordinat ved å velge den runde "sprite x"-blokka fra **Spill**-menyen. Du kan endre X til Y for å finne y-koordinaten. {{url>https://jsfiddle.net/royeven/b57f9nke/11/embedded/css,html,result/ 100%,400 noscroll noborder left}} ===== Markere det bombede feltet som bombet ===== Dette trinnet er valgfritt. Mange pleier å krysse ut felt man har bombet for å gjøre det lettere å huske hvilke felt man allerede har bombet og hvilke som må bombes. Dersom du ønsker å farge felt du allerede har bombet kan du opprette en ny brikke på den X- og Y-koordinaten du har bombet og angi en lav lysstyrke på denne brikken. Da vil de bombede feltene gløde svakt. Etter at du har sendt X- og Y-koordinaten til det bombede feltet til fienden kan du lage en ny variabel (kall den gjerne //bombet//) og bruk denne til å lage en ny brikke på samme X- og Y-koordinat som //sprite//. Angi //bombet// sin lysstyrke til f.eks. 10 {{url>https://jsfiddle.net/royeven/b57f9nke/12/embedded/css,html,result/ 100%,400 noscroll noborder left}} ===== Motta bomber fra den andre spilleren ===== For at dette skal bli rettferdig må du også sjekke om de koordinatene den andre spilleren bomber treffer dine skip. Ettersom spillet er symmetrisk vil motspilleren din sende deg sammensatte tekster med samme format som du sendte til ham. Du må altså utvide spillet ditt med en "Når radio mottar tekst"-blokk til å håndtere innkommende bomber fra motspilleren. Du trenger to variabler til å lagre X- og Y-koordinaten som motspilleren din bomber. Kall disse //x// og //y//. Du trenger en "når radio mottar tekst"-blokk fra **Radio**-menyen for å motta tekster og du trenger kommandoer fra **Tekst**-menyen for å dekode tekstene du mottar. Løsningsforslaget legger opp til at informasjonen om hvor en spiller bomber skal sendes på formatet | X-koordinat | "x" (bokstaven x) | Y-koordinat | Teksten består altså av tre deler og hver del består av ett tegn. De tre delene er satt sammen til en tekst på totalt tre tegn, f.eks. "0x4" (tilsvarer X=0,Y=4) og "3x2" (tilsvarer X=3,Y=2). Hvert tegn står på en fast plass i teksten, f.eks står x-koordinaten alltid først i teksten, og y-koordinaten sist i teksten. Vi mennesker er vant med at det første tallet i en rekkefølge er 1, deretter følger 2 og 3 osv. I de fleste programmeringsspråk begynner man derimot på 0 før 1, 2 og 3 følger. Det betyr at formatet på teksten du skal dekode blir: | Posisjon | 0 | 1 | 2 | | Tegnet på den aktuelle posisjonen | X-koordinat | "x" (bokstaven x) | Y-koordinat | Du kan bruke kommandoen "del av tekst ved posisjon" til å hente ut en bestemt del av teksten, og ettersom alle delene består av kun ett tegn skal lengden på hver del du henter ut være 1. Deretter må du konvertere (altså gjøre om) tegnet til et tall: {{url>https://makecode.microbit.org/---docs?md=%20%60%60%60blocks%0D%0Alet%20x%20%3D%20null%0D%0Alet%20x_koordinat%20%3D%20%22%22%0D%0Aradio.onReceivedString(function%20(receivedString)%20%7B%0D%0A%20%20%20%20x_koordinat%3DreceivedString.substr(0%2C%201)%0D%0A%20%20%20%20x%20%3D%20parseFloat(x_koordinat)%0D%0A%7D)%0D%0A%20%60%60%60 100%,400 noscroll noborder left}} Om ønskelig kan du slå sammen de to delene ovenfor til følgende: {{url>https://makecode.microbit.org/---docs?md=%20%60%60%60blocks%0D%0Alet%20x%20%3D%20null%0D%0Aradio.onReceivedString(function%20(receivedString)%20%7B%0D%0A%20%20%20%20x%20%3D%20parseFloat(receivedString.substr(0%2C%201))%0D%0A%7D)%0D%0A%20%60%60%60 100%,400 noscroll noborder left}} Når radio mottar tekst vil denne teksten bli lagret i variabelen //receivedString//. Denne sammensatte teksten må du dekode for å hente ut X- og Y-koordinaten til det bombede feltet. Lag deg to variabler som du kaller //x// og //y//, og lagre de to koordinatene fra //receivedString// i disse to variablene. Du må bruke kommandoen "del av received string fra posisjon 0 med lengde 1" for å få ut den første koordinaten (altså X-koordinaten og "del av received string fra posisjon 2 med lengde 1" for å få den andre koordinaten (altså Y-koordinaten). De ulike delene av teksten på gjøres om til tall (du finner funksjoner for dette i **Tekst**-menyen i **Avansert**) og lagres i variablene //x// og //y//. {{url>https://jsfiddle.net/royeven/b57f9nke/13/embedded/css,html,result/ 100%,400 noscroll noborder left}} ===== Sjekke om du har et skip der motspilleren bomber ===== Hvis du har et skip på den X- og Y-koordinaten fienden bomber må du fjerne dette skipet fra tabellen //mine_skip// samt sende en melding til fienden (bruk "radio send tall"-kommandoen) så han vet at han traff. Du må også miste ett liv for hvert skip fienden senker. Når alle dine skip er senket er spillet over. Du kan bruke en "for element //verdi// av //mine_skip//-løkke til å gå gjennom alle dine skip og se om noen av dem er truffet. Hvis et skip er truffet må det fjernes fra tabellen //mine_skip//. {{url>https://makecode.microbit.org/---docs?md=%20%60%60%60blocks%0D%0Alet%20x%20%3D%20null%0D%0Alet%20mine_skip%3A%20game.LedSprite%5B%5D%20%3D%20%5B%5D%0D%0Aradio.onReceivedString(function%20(receivedString)%20%7B%0D%0A%20%20%20%20x%20%3D%20parseFloat(receivedString.substr(0%2C%201))%0D%0A%20%20%20%20for%20(let%20verdi%20of%20mine_skip)%20%7B%0D%0A%20%20%20%20%20%20%20%20if%20(x%20%3D%3D%20verdi.get(LedSpriteProperty.X))%20%7B%0D%0A%20%20%20%20%20%20%20%20%20%20%20%20mine_skip.removeAt(mine_skip.indexOf(verdi)).delete()%0D%0A%20%20%20%20%20%20%20%20%7D%0D%0A%20%20%20%20%7D%0D%0A%7D)%0D%0A%20%60%60%60 100%,400 noscroll noborder left}} I en slik løkke vil //verdi// tilsvare ett skip, og du kan behandle //verdi// som en vanlig brikke. Bl.a. kan du angi koordinater og lysstyrke til //verdi//, du kan hente ut //verdi// sine koordinater og du kan gjøre alt du kan gjøre med andre brikker. Hvis du har tre skip vil denne løkken bli gjentatt tre ganger: første gang vil //verdi// være det første skipet i tabellen //mine_skip//, andre gang til //verdi// være det andre skipet i tabellen, og tredje gang vil //verdi// være det siste skipet i tabellen. Hvis verdi sin y-koordinat er lik den Y-koordinaten fienden bombet og verdi sin X-koordinat også er lik den X-koordinaten fienden bombet må du slette brikken //verdi// fra tabellen "//mine_skip//. Dette er litt avansert og krever at du finner "indeks av //verdi//" (altså om //verdi// ligger på plassering 0, 1 eller 2 i tabellen) og fjerner denne indeksen fra tabellen //mine_skip//. Brikken må også slettes, og du finner de funksjonene du trenger fra **Tabell**-menyen og i **Spill**-menyen. Du kan hente inspirasjon fra bildet over på hvordan du kan gjøre det. Hvis fienden traff ett av dine skip må du fjerne ett av livene dine (du finner kommando for dette i **Spill**-menyen) samt bruke radioen til å sende et tall (f.eks. 1) til fienden for å fortelle fienden at han traff. {{url>https://jsfiddle.net/royeven/b57f9nke/14/embedded/css,html,result/ 100%,400 noscroll noborder left}} ===== Når du treffer fiendens skip ===== Ettersom spillet er symmetrisk, vil fienden gjøre akkurat det samme som du gjør. Dersom du treffer ett av fiendens skip vil du også motta tallet 1 via radio. Når det skjer skal du gi deg selv ett ekstra poeng. Dersom du har like mange poeng som det antallet skip dere startet med har du senket alle fiendens skip og spillet er derfor over. Du kan bruke "når radio mottar //receivedNumber//" til å håndtere senkede fiendeskip. Når du mottar et tall kan du endre din poengsum med 1. Hvis poengsummen din nå er lik //antall_skip// kan du avslutte spillet med "spillet er slutt"-kommandoen (game over) fra **Spill**-menyen. {{url>https://jsfiddle.net/royeven/b57f9nke/15/embedded/css,html,result/ 100%,400 noscroll noborder left}} ====== Egen innsats ====== Den årvåkne programmerer vil ha merket seg noen svakheter i dette spillet: * Spilleren kan ikke selv velge hvor skipene skal plasseres. Kanskje kan du endre programmet ditt slik at du ved start kan plassere skipene på samme måte du plasserer bomber senere i spillet? * Slik spillet er nå er det fullt mulig for deg å bombe mange felt etter hverandre uten å la medspilleren bombe tilbake. For at spillet skal være rettferdig må man sørge for at de to spillerne bomber hverandre annenhver gang. Kan du legge inn kontrollmekanismer i spillet slik at når spiller 1 har bombet ett felt ikke kan bombe et annet før spiller 2 har bombet ett felt? * I spillet slik det er gjennomført i denne oppskriften vil skipene bli plassert helt tilfeldig. Dersom du lager mange skip pr. spiller er det stor sannsynlighet for at to skip vil bli plassert oppå hverandre. Hvordan kan man best unngå dette? * Hvis du ikke har en medspiller greier du kanskje å endre programmet slik du spiller mot datamaskinen? Micro:biten kan programmeres til å bombe dine skip på tilfeldig valgte koordinater. * I den versjonen av spillet som du har programmert kan du bombe så mye du vil. Greier du å endre spillet slik at du har et begrenset antall bomber?