Föreläsningar nov 19, nov 20 v 47. Trådar, Undantag, DFA mm. Att göra ett dator-femtonspel. Ett "klick"-styrt grafiskt femtonspel som dessutom ändrar sig själv. I "vickspelet" vill vi att datorn med jämna tidsintervall skall anropa metoden tick(..) i föremålet av typ Gadget. Vi behöver en "klocka" som oberoende av programmet i övrigt med intervallet deltat sekunder anropar tick(deltat). Metoden tick(double deltat) avläser dragreglagen, räknar ut föremålets nya läge efter tiden deltat och ritar ut spelets nya utseende. I Java kallas en följd instruktioner som datorn kör för en tråd (engelsk thread). I alla ickegrafiska program hittills har våra datorprogram bara bestått av en enda tråd. Traditonellt brukar man i elementära programmeringskurser bara studera sådana program. I våra femtonspel har applikation startat med anropet till main vars körning varit en tråd som ritat femtonspelet, varefter tråden slutat. Varje klickning på en plats (bricka) har startat en ny kortlivad liten tråd som har kör koden i actionperformed. Metoden actionperformed har anropat flytta(..) i femtonspelet som ändrat femtonspelet om vi klickat platsen intill tomma platsen. Därefter har tråden slutat. I vickspelet behöver vi en tråd som fungerar som en klocka och t ex varje sekund anropar förmålets metod tick(...). För att visa hur sådan klocka programmeras skall jag förse vårt femtonspel med en oberoende tråd, en klocka, som varje sekund försöker flytta en slumpvist vald bricka. Att spela SpelaFemtonspel4 är alltså svårare eftersom "någon varje sekund lägger sig i". Eftersom troligheten att den slumpvist valda brickan är intill tomma brickan är ungefär 0.25 så ändras spelet gernomsnittligt ca var fjärde sekund. Ändrar man klockans periodtid till delar av sekunder är säkert spelet blodtryckshöjande. Att programmera trådar brukar, tros att sådan programmering är vanlig i praktiken, anses som avancerad programmering och studeras i kurser som brukar kallas "realtidsprogrammering", "reaktiv programmering", "parallellprogrammering" eller dylikt. I de följande beskrivs ej trådprogrammering ordentligt. Jag använder ideer som beskrivs i kap 9 i Jan Skansholms b
Java direkt, som varmt rekomenderas för den intresserade. I Java ingår språkelement för trådprogrammering i språket, vilket är ovanligt. I DD behandlas trådar i kap 14. I denna kurs hoppas jag det räcker med att läsa detta för att kunna programmera"vick-spelet". Man behöver inte förstå alla detaljer om hur den givna klassen för att göra en klocka är programmerad. Vi programmerar klockor med en klass LKClock så att klockor kan påverka alla objekt som har en metod tick(double time). För att understödja detta inför vi ett gränsnitt interface Ticked { public void tick(double deltat); och skriver en konstruktor i LKClock så att en klocka när den skapas med får veta vilket objekt av typen Ticked den ska en kan påverka med tick(..) och hur ofta : class LKClock implements Runnable { public Thread aktivitet = new Thread(this); // ny tråd // tråden startas med aktivitet.start() och datorn utför då // instruktionerna i metoden run i denna (this) instans private Ticked obj; private double ticktime = 1; //påverka detta objekt //påverka med detta intervall s public LKClock(Ticked iobj, double iticktime) { obj = iobj; ticktime = iticktime; public void run() { // anropas indirekt vid aktivitet.start(); while(true) { // klockan går i evighet try { // sleep kan kasta undantag som måste fångas Thread.sleep((int) (ticktime*1000)); // tråden "sover" // ticktime*1000 ms catch (InterruptedException e) { obj.tick(ticktime); //påverka objektet (tex femtonspelet) Men nu skall vårt femtonspel påverkas av en klocka. Vi kompletterar vår klass Femtonspel med en instans lkclock av en klass LKClock och en ny metod tick(double time). Objeketet lkclock anropar ju tick(...).
import java.util.*; // SpelaFemtonspel4 som SpelaFemtonspel3 class Femtonspel extends Frame implements Ticked { // som förut private LKClock lkclock; public Femtonspel() { // som förut lkclock = new LKClock(this); lkclock.aktivitet.start(); // startar klockan // som blir en oberoende tråd public void tick(double deltat) { int k = (int) (16.0*Math.random()); flytta(k); // som förut Ytterligare ett exempel på ett klockstyrt objekt : En TeknologTamagotchi En Teknologtamagotchi är ett datorprogram som fungerar som ett lite gosedjur (en cirkel) som man skall försöka få så stort som möjligt. Genom att trycka på knapparna kan man tillfredställa dess behov och på så sätt få den att växa. Vissa sekvenser av knapptryckningar skall vara bättre än andra. Till exempel är sekvensen sova - tenta - festa en kombination som premieras med extra stor tillväxt medan sekvensen festa - sova - tenta är en dålig kombination som leder till minskad storlek. Om samma sak händer tre gånger i rad blir tamagotchin uttråkad och minskar sin radie till hälften Gör också så att tamagotchin blir uttråkad och drar ihop sig om inget händer inom 6 sekunder. - Teknolog-tamagotchi. Tenta Festa Sova Plugga
Vi gör en design där vi skiljer på modellen av tamagotchin och bilden av tamagotchin. Att göra så är att tillämpa ett design mönster - design pattern - som kallas Model- View-teknik. Det finns i Java bibllioteket stöd för detta design mönster i form av klasserna Observable och Observer, men jag använder inte dessa klasser här. Objektetet som är bilden av tamagotchin har jag också gjort till lysnnare på de fyra knapparna. Eftersom då detta objekt lyssnar på fyra olika komponenter (de fyra knapparna) måste vi använda metoden getsource när vi skriver actionperformed. Växelspelet mellan instansvariabeln count och metoden gets(char b) kan belysas med denna bild: deterministisk finit automat (DFA) c radie = 0.3*radie c c c c c count=0 count=1 count=2 count=3 count=4 count=5 Ett klass diagram finn efter nedanstående implementation. Impelemntation : import java.awt.*; import java.awt.event.*; public class PlayTamagotchi { public static void main(string [] arg) { Tamagotchi t = new Tamagotchi(); TView tv = new TView(t); class Tamagotchi implements Ticked { private int radie = 40; private Sequence seq = new Sequence(); private CPanel cpanel; private LKClock clock; private int count = 0; public Tamagotchi() { clock = new LKClock(this, 1); clock.aktivitet.start(); public void tick(double itime) { gets( c ); public void gets(char b) { if (b == c ) { count = count + 1; if (count == 6) { count = 0; radie = (int) (((double) radie)*0.3); cpanel.update(radie);
else { count = 0; seq.add(b); radie = (int) (((double) radie)*seq.getfactor()); cpanel.update(radie); public void setview(cpanel icp) { cpanel = icp; class Sequence { private char vold; private char old; private char act; public void add(char ib) { vold = old; old = act; act = ib; public double getfactor() { System.out.println("NU = " + vold + old+act); if (vold == old && vold ==act) { return 0.5; else if (vold == s && old == t && act == f ) { return 1.5; else if (vold == f && old == s && act == t ) { return 0.5; else if (vold == f && old == f && act == t ) { return 0.1; else if (vold == p && old == s && act == t ) { return 2.0; else { return 1.0; class TView extends Frame implements ActionListener { private Tamagotchi t; private CPanel cpanel; private Button tenta = new Button("Tenta"); private Button festa= new Button("Festa"); private Button sova= new Button("Sova"); private Button plugga= new Button("Plugga"); public TView(Tamagotchi it) { super("teknolog-tamagotchi"); t = it; setsize(400,400); setlayout(new BorderLayout()); cpanel = new CPanel(); add("center", cpanel); add("north", tenta); add("west", festa); add("east", sova); add("south", plugga); tenta.addactionlistener(this); festa.addactionlistener(this); sova.addactionlistener(this); plugga.addactionlistener(this); setvisible(true); t.setview(cpanel); t.gets( );
public void actionperformed(actionevent e) { if (e.getsource() == tenta) { t.gets( t ); else if (e.getsource() == festa) { t.gets( f ); else if (e.getsource() == sova) { t.gets( s ); else if (e.getsource() == plugga) { t.gets( p ); else { class CPanel extends Panel { private int r = 10; public CPanel() { setvisible(true); public void paint(graphics g) { g.setcolor(color.red); g.filloval(150-r, 150-r, 2*r, 2*r); public void update(int ir) { r = ir; repaint();
PlayTamagotchi JPanel public static void main(..) { Tamagotchi t TView tv Frame ActionListener CPanel private int r = 10; public CPanel() public void paint(graphics g) public void update(int ir) TView private CPanel cpanel private Tamagotchi t private Button tenta private Button festa private Button sova private Button plugga 4 Button Sequence public TView(Tamagotchi it) public void actionperformed(...)...... private char vold; private char old; private char act; public void add(char ib) public double getfactor() Ticked LKClock Runnable public Thread aktivitet private RTicked obj: private double ticktime public LKClock(Ticked ioj, double iticktime) public void run() Tamagotchi private int radie private Sequence seq = new Sequence(); private CPanel cpanel; private LKClock clock; private int count public Tamagotchi() public void tick(double itime) public void gets(char b) public void setview(cpanel icp) Översikt över awt och swing paketen. Undantag (Exceptions) Kontrollskrivning 1. Dessa avsnitt finns endast ipappersupplagan)
Hemuppgifter redovisning v48 (preliminärt). 1. CBA-hemuppgift : Implementera ett "vick-spel" ett grafiskt användargränsnitt (GUI) enligt nedanstående figur (ungefär): läget för föremålet (typ Gadget, inte en grafisk komponent), en liten cirkel brädet (av typ Board en sorts JPanel) dragreglage (typ JSlider) dragreglage (typ JSlider) Om man med musen påverkar dragreglagen så innebär det att brädet lutas och föremålet accelereras. Spelet går ut på att få föremålet i hålet och undvika att föremålet hamna utanför brädet. Reglerna för föremålets rörelser beskrivs i tidigare hemuppgifter. Så här gjorde jag: Min utfomning av lösningen antyds av klassdiagrammet på nästa sida. Det blev väl mycket "känner till pilar" men jag kommer inte på någon mer elegant design på stubben. Använd BorderLayout som i förra veckans hemuppgift för att bygga ett Game (allt i bilden ovan) som är ett JFrame. Ett Game har också en klocka LKClock och ett föremål av typ Gadget. Klockan är en tråd som startas (sist i konstruktorn Game när allt är klart för start). När klockan instansieras får den reda på vilket objekt den skall "ticka" (i vårt fall föremålet av typ Gadget)och med vilket intervall. Klockan "tickar", dvs anropar tick i förmålet. Metoden tick i föremålet frågar då spelet efter vinklarna (dvs inställningarna på de två dragreglagen av tvp JSliders) och en ny position och en ny hastighet räknas ut för föremålet. Metoden tick i föremålet kollar därefter med brädet om det kommit i hålet eller utanför brädet, i så fall återplacerar föremålet sig själv i startläge. Till sist så beordrar metoden tick i föremålet omritning av brädet. Provkör min lösning på /info/inda01/game/cba genom att ge kommandot java PlayGame. På katalogen finns också mina förslag till LKClock och Ticked med källkod.
PlayGame public static void main(..) { Game game JPanel JFrame ChangeListener Board private final double scale (// pixels/m private int boardsizeinpixels; private double boardsize; private double gadgetsize; private Vector holecenter private double holeradius; private Gadget gadget public Board(Gadget ig) public void paintcomponent(graphics g) // ritar om brädet public boolean outsideboard(vector p) public boolean insidehole(vector p) public Dimension getpreferredsize() public Dimension getminimumsize() Game private JSlider alfaslider private JSlider betaslider private Board board private LKClock clock private Gadget gadget private double alpha private double beta public Game() public void resetsliders() public void statechanged(changeevent e) public getalpha() public getbeta() 2 JSlider...... Ticked LKClock Runnable public Thread aktivitet private RTicked obj: private double ticktime public LKClock(Ticked ioj, double iticktime) public void run() Gadget private Vector p private Vector v Game game Board board; public static final double g = 9.81 public void tick(double deltat) public void resetat(vector ip, Vector iv) public void getwhere();
2. B-hemuppgift : Implementera ett Luffarschack med GUI enligt nedanstående fig (ungefär): x o Knapparna med har blivit klickade. Använd GridLayout. Jag använde awt-paket, inte swing som jag inte är lika van med, men du kan välja vilket paket du vill. I början gör en klickning på någon av de nio knapparna i 3*3 matrisen gör att knappen markeras växelvis med ett x eller ett o. När tre x och tre o blivit placerade måste spelarna växelvis göra två klickningar : på rutan varifrån de vill flytta och på en tom angränsande ruta dit de vill flytta. Spelet vägrar göra felaktiga drag, om man ändå försäker får man felutskrifter. Spelet avslutas när någon spelare får "tre i rad", diagonalt eller rakt Provkör min lösning på /info/inda01/gui/b med kommandot java SpelaLSchack. Det är OK att förfina lösningaen en hel del, t ex att felmedelande skrivs i GUI och ej i terminalfönstret, spelet avslutas (eller återstartas) mer elegant när någon får "tre i rad" osv. Att programmera vad som skall hända när man klickar på en ruta är något komplicerat. Spelet har ju en första fas när spelarna växelvis med ett klick placerar sina x och o, och en andra fas där spelarna växelvis skall göra två klickningar, först klicka på en ruta med rätt markerering och sedan klicka på en angränsande tom ruta dit x eller o skall flyttas. Eventuellt har du glädje av DFA:n på nästa sida, som är något förenklad och skissartad. betyder att man klickar enligt spelets regler.
deterministisk finit automat (DFA) antaleto < 3 krysstur = true antaletx = antaletx +1 antaleto < 3 krysstur = false, antalet0 <3 antalet0 = antalet0 +1 gameover, antalet0 = 3 antalet0 = antalet0 +1 antaleto = 3 krysstur = true first = true antaleto = 3 krysstur = true first =false antaleto = 3 krysstur = false first = true antaleto = 3 krysstur = false first = false 3. A-hemuppgift : Implementera ett GUI som är ett spelbart schackbräde. Spelarna ska växelvis göra två klickningar : på rutan varifrån de vill flytta och på en tom angränsande ruta dit de vill flytta Brädet vägrar utföra felaktiga drag, om man ändå försäker får man felutskrifter. Dock frivilligt att göra rockad och en passent. Man behöver inte heller upptäcka schack och schak-matt och patt. Använd GridLayout. Jag använde awt-paket, inte swing som jag inte är lika van med, men du kan välja vilket paket du vill. Provkör min lösning på /info/inda01/game/a med kommandot java PlayChess. Det är OK att förfina lösningen en hel del, t ex att felmedelande skrivs i GUI och ej i terminalfönstret, rita riktiga schackpjäser (bilder går att hitta på nätet) istället för att skriva pjäsernas namn osv.