CSC

 

Laboration 3 - Sten, sax, påse

Ett grafiskt klientprogram där en användare spelar mot en server

På föreläsning 5 eller 6 visas ett serverprogram som spelar sten, sax, påse med alla villiga klienter. Koden finns här: Server4712.java. Nu ska du skriva ett grafiskt klientprogram och du ska använda idel swingkomponenter, nämligen JFrame, JTextField o/e JLabel, JButton, ImageIcon och minst en av JPanel och Box. Det är fritt fram att använda andra komponenter! Spelet är känt över hela världen och det finns mycket att hämta på webbsidan Rock, paper, scissors, bland annat tre gif-bilder.

Om servern skulle vara nere eller uppvisa något annat problem så går det bra att som nödlösning kopiera server-programmet till den egna datorn och köra det där. Instruktioner finns här: lokalserver.html

Server utan svenska bokstäver En version av serverprogrammet som är helt fri från åäöÅÄÖ kör genom port 4713, koden finns här: Server4713.java. Den spelar STEN, SAX och PASE.

En enkel klient

Börja med att testa en enkel klient utan grafik, som öppnar en förbindelse med
   try {
       Socket socket=new Socket("p6-ray.nada.kth.se",4712);
       BufferedReader in=new BufferedReader
           (new InputStreamReader(socket.getInputStream()));
       PrintWriter ut=new PrintWriter(socket.getOutputStream());
       ut.println("Charlotta"); ut.flush();
       System.out.println(in.readLine());
   }
Klienten ska sända ett namn (här sänds Charlotta) till den server som lyssnar på port 4712 och skriva ut det svar den får. Kom ihåg ut.flush();, annars sänds inte namnet iväg. Koden ovan behöver kompletteras en hel del för att gå att köra överhuvudtaget men även för att testa servern. Bland annat bör man skicka ytterligare några meddelanden till servern och som svar få "STEN", "SAX" eller "PÅSE". För att avsluta förbindelsen ska man skicka något som servern läser som null eller tom sträng. Det fungerar t.ex. med tom sträng (""), Ctrl-D eller Ctrl-Z. Se till att den enkla klienten fungerar innan ni går vidare och ägnar er helt åt det grafiska gränssnittet.

Swingkomponenterna

Ditt program kan (men behöver inte) vara en
public class Klient extends JFrame implements ActionListener{
där vänstra halvan av fönstret är din spelplan och högra halvan är datorns spelplan. De två halvorna är tillräckligt lika för att kunna representeras av objekt av samma klass. När klassen skrivs, använd gärna BoxLayout, den är någorlunda flexibel men ändå enkel och blir oftast bra! Om du är helt ovan vid Javas grafik så kan det vara bättre att välja den mer grundläggande GridLayout. Se nästa avsnitt! Med BoxLayout så bör varje spelplan vara en vertikal Box som uppifrån och ner inneåller
  • Rubrik, en JLabel med texten Jag: eller Datorn:.
  • Meddelande, en JTextField där det som sänds från dej och från datorn syns. Först visas hälsningen, därefter det senaste draget, dvs STEN, SAX eller PÅSE.
  • En knapp med en stenbild
  • En knapp med en saxbild
  • En knapp med en pappersbild
  • Resultat, en JLabel som efter ett tryck visar ETT..., efter andra trycket TVÅ... och efter tredje VINNER, OAVGJORT eller FÖRLORAR (förklaras mer i stycket om Spelet).
  • Poäng, ett JTextField som visar t.ex. Poäng: 7
Det går att använda JLabel för alla de små textfälten ovan men i vissa sammanhang är det lättare med JTextField när man måste ha fält som är tomma från början. En egen klass för Spelplan bör definieras. Den kan gärna vara subklass till Box och i sin konstruktor anropa super(BoxLayout.Y_AXIS); för att markera att den är vertikal. Två objekt av den skapas och läggs bredvid varandra i t.ex. en horisontell box. En av fördelarna med en box är att man kan lägga in fasta mellanrum mellan komponenterna Om man deklarerat
   Box box=Box.createHorizontalBox();
kan man göra så här.
   Spelplan jag = new Spelplan("Jag:");
   Spelplan du = new Spelplan("Datorn:");
   box.add(jag);
   box.add(Box.createHorizontalStrut(20)); //Ett mellanrum
   box.add(du);

Box-objektet läggs därefter in i JFrame-objektet med metoden add.

Alternativ till Box för spelplanerna

Om du är ovan vid Javas grafik kan det vara lika bra att använda en JPanel med GridLayout för spelplanerna. Det går att få riktigt snyggt det också! Varje spelplan kan delas in i 5 lika stora fält där knapparna läggs i de tre mittersta. Översta och nedersta fälten får var sin JPanel som i sin tur delas in flera fält. Om man lägger in en tom JPanel så fungerar det som ett mellanrum. Inte fullt så flexibelt som i en Box men helt OK.

Om du väljer detta alternativ för spelplanerna så använd även en JPanel med GridLayout för det omgivande fönstret, alltså det fönster där "jag" och "du" läggs in i exemplet ovan.

Spelet

Spelet går till så att användaren trycker på sten, sax och påse-knapparna och vid tredje trycket läser programmet av vilken knapp som tryckts på och draget STEN, SAX eller PÅSE registreras. Det spelar ingen roll vilka knappar man trycker på de två första gångerna och de knapparna behöver inte markeras med färgskifte men texten "ETT..." respektive "TVÅ..." ska visas i resultatrutan. De verkningslösa tryckningarna ska efterlikna att man i det manuella spelet slår med en knuten hand i luften två gånger (räknar ett, två) och därefter (på tredje slaget) visar sitt drag. I samband med användarens tredje tryck ger också datorn sitt drag (dvs programmet läser av draget från servern). Programmet avgör om det är oavgjort eller vem som vunnit samt uppdaterar Resultat-rutan och Poäng-rutan.

Några tips

  • Om du låter klassen Spelplan ärva Box kan du i konstruktorn börja med anropet
    super(BoxLayout.Y_AXIS) för att få den vertikal.
  • Bilderna kan tillverkas redan i deklarationen ImageIcon sten=new ImageIcon("rock.gif") och samlas i en array Icon[] bild={sten,sax,påse}.
  • Man kan ha användning för en textarray String[]text={"STEN","SAX","PÅSE"}.
  • I spelplansklassen finns det nog en knapparray. Fastän det inte står text på knapparna kan man anknyta en text med knapp[i].setActionCommand(text[i]). Den kan man sedan få fram i actionPerformed med e.getActionCommand() (Trevligt, men inte absolut nödvändigt i labben).
  • Avgivna drag ska markeras så att den valda knappen ändrar färg (eller annat utseende). Då kan en metod public void markera(...) i spelplansklassen vara lämplig. Någon eller några parametrar behövs i metoden men det beror ju på hur mycket metoden ska göra (bara markera färg eller även uppdatera fälten).
  • För varje gång användaren avger sitt drag tar programmet emot ett servermeddelande som skickas till metoden markera i datorfönstret.
  • Det behövs en global variabel som räknar till tre och sedan återgår till noll. Varje spelare ska ha en poängvariabel. Var läggs den lämpligen?
  • Varje klick som är ett drag skall föregås av två verkningslösa klick. Klicken räknas och deras enda effekt är att texterna "ETT..." respektive "TVÅ..." visas i resultatrutan.
  • Här ställs inte kravet att hålla strikt på Model-View-Control (som i förra labben) och göra en modell som kan köras fristående i terminalfönstret. Skälet är att modellen korrekt representrad är så försvinnande liten, det mesta handlar här om View och Control som hänger intimt ihop. Det är dock inte alls fel att göra en egen modell, särskilt om man tycker att den verkar lite krånglig.
  • En knapp som kopplar ner förbindelsen med servern och avslutar programmet är trevligt att ha men inget krav.

Redovisning och krav på programmet

  • Demonstrera SSP-spelet som använder det givna serverprogrammet för att få fram datorns drag. Spelplanerna ska se ut så som beskrivs ovan.
  • Programmet måste bestå av minst två klasser och de två spelplanerna ska vara objekt av samma klass.
  • Vid varje drag skall det synas hur användaren respektive datorn har spelat genom att knapparna ändrar färg (se tipsen!) och genom att dragets text skrivs ut i meddelanderutan.
  • Efter varje drag visas också vem som vunnit (eller om det är OAVGJORT) samt aktuell poängställning.
  • Ett drag utförs vid var tredje av användarens knapptryckningar. Första trycket visar "ETT..." i resultatrutan, andra trycket visar "TVÅ..." och tredje trycket ger draget.
  • Logiken i programmet ska vara noga genomtänkt så att det inte blir onödigt långt och krångligt. T.ex. att ta redo på vem som har vunnit eller om det var oavgjort kan skrivas med 9 if-satser eller med bara två. Lösningar utan if är också möjliga. Kodupprepning skall undvikas!
  • Uppdatering av spelplanerna ska ske genom anrop av ett fåtal metoder.
  • Även punkterna 1 och 2 från följande checklista ska redovisas.

Som extrauppgift väljes en av följande

Extrauppgift, alternativ 1: Lägg till ljudeffekter! Kan kräva hörlurar. Det bör vara minst tre olika ljud (spel, vinst, förlust). Ljudet ska kunna slås av och på interaktivt medan man kör programmet.

Extrauppgift, alternativ 2: Bygg ut det grafiska gränssnittet så att uppkopplingen till servern styrs därifrån. Låt användaren välja serverdator och portnummer, antingen genom att det finns några explicita att välja på eller skriva in namnet (t.ex. "p6-ray.nada.kth.se") och portnumret i inmatningsfält.

Sidoansvarig: <ann "at"nada.kth.se>
Senast ändrad 23 april 2010
Tekniskt stöd: <webmaster@nada.kth.se>