package org.ais_sanmarino.ludoj.robotoj; import java.awt.*; import java.awt.event.KeyListener; import java.awt.event.WindowListener; import java.awt.event.ActionListener; import java.awt.event.KeyEvent; import java.awt.event.WindowEvent; import java.awt.event.ActionEvent; import java.applet.Applet; /** *

* Tiu cxi klaso realigas malnovan ekran-ludon, * kiu ekzistis jam en fruaj UNIX-generacioj. * Gxi tie nomigxis robots kaj estis ludebla sur * negrafikaj ekranoj. * Ekzistas ankaux grafika versio nomata xrobots. *

*

* La ludanto povas movi ludpecon (la "fugxanton") en la * ok ekran-direktoj, cxiam al najbara kampo. * Li povas ankaux "transporti" gxin al hazarde elektita kampo. * La celo de la ludo estas eviti malicajn robotojn, * kiuj volas kapti kaj mortigi la fugxanton. * Ili movigxas cxiam direkte al la fugxanto, * kaj kiam ili kolizias kun gxi aux kun alia roboto, * ili eksplodas. *

*

* La sxanco de la ludanto estas direkti robotojn al aliaj robotoj, * ankaux al jam eksplodintaj. * Se li sukcesas detrui cxiujn robotoj, li gajnas, * sed ankoraux ne havas poentojn. * Por akiri tiajn li devas kuragxi "atenti", * t. e. iumomente rezigni je sia eblo movi la fugxanton * kaj simple lasi la robotoj kuri (espereble al sindetruo). * Por cxiu roboto ekzistanta en tiu momento de la decido atendi * li ricevas unu poenton. * Poentoj akumuligxas, sed cxe morto oni (kompreneble) perdas * cxiujn poentojn. *

*

* Pro sia UNIX-deveno la ludo uzas la manovrajn klavojn de * vi. * Por movi la fugxanton en la ok direktojn eblas uzi jenajn klavojn: *

 *     y k u
 *     h . l
 *     b j n
 * 
*

* La punkto (.) kauxzas unuciklan atendon. * Majuskloj ripetigas movon tiom longe, kiom eblas (sen kolizii kun io aux io). * Por uzantoj de Germanaj klavaroj la klavo z efikas egale * al y * (kiu en Usonaj klavaroj situas tie, * kie en Germanaj situas la z). *

*

* Krome eblas jenaj komandoj: *

*

*

* Havu plezuron ludante! *

* * @author Reinhard Fössmeier * @version 1.0 (2001-11-25) */ public class Robotoj extends Applet implements WindowListener { /** * interfaco por klasoj, kiuj ricevas komandojn. */ interface Komandato { /** plenumu donitan komandon. */ void plenumu( int komando ); } /** * interfaco por klasoj, kiuj povas raporti al la uzanto detalojn * pri la ludo. */ interface Mesagxisto { /** montru donitan mesagxon. */ void mesagxu( String t ); /** montru akiritajn poentojn. */ void montruPoentojn( int po ); /** montru la maksimumon de (iam) akiritaj poentoj. */ void montruMaksimumajnPoentojn( int mpo ); } /** rezervenda alteco por la butonaro */ private final int butonAlteco = 35; /** * La ludkampo. * Temas -- pro historiaj kauxzoj -- pri kampo de 80 oble 24 kampetoj * inter kiuj movigxas la lud-pecoj. */ protected Kampo kampo = new Kampo(); /** * La programo ekrulas en memstara fenestro. */ public static void main( String par[] ) { Frame kadro = new Frame(); Robotoj rob = new Robotoj(); Butonaro bu = rob.new Butonaro( rob.kampo, true ); kadro.setSize( 500, 300 ); kadro.setLayout( new BorderLayout() ); kadro.add( rob.kampo, BorderLayout.CENTER ); kadro.add( bu, BorderLayout.NORTH ); kadro.addWindowListener( rob ); bu.addKeyListener( rob.kampo ); rob.kampo. metuMesagxiston( bu ); kadro.setVisible( true ); /* 1.0: show(); */ rob.kampo.requestFocus(); } /** * La programeto (apleto) ekrulas. * Videbligu la fenestron kaj ekludigu! */ public void init() { Butonaro bu = new Butonaro( kampo, false ); setLayout( new BorderLayout() ); add( kampo, BorderLayout.CENTER ); add( bu, BorderLayout.NORTH ); bu.addKeyListener( kampo ); kampo. metuMesagxiston( bu ); setVisible( true ); /* 1.0: show(); */ Dimension gr = getSize(); gr.height -= butonAlteco; kampo.setSize( gr ); } /** * Pentru la lud-kampon (kradon). */ public void paint( Graphics g ) { kampo.paint( g ); kampo.requestFocus(); } public void windowOpened( WindowEvent e ) { } public void windowClosing( WindowEvent e ) { e.getWindow().setVisible( false ); System.exit( 0 ); } /** * Fermigxas la fenestro, finu la programon. */ public void windowClosed( WindowEvent e ) { System.exit( 0 ); } public void windowIconified( WindowEvent e ) { } public void windowDeiconified( WindowEvent e ) { } public void windowActivated( WindowEvent e ) { } public void windowDeactivated( WindowEvent e ) { } /** * tiu cxi klaso provizas la ludkampon, * sur kiu movigxas la lud-pecoj. */ class Kampo extends Canvas implements KeyListener, Komandato { /** * ripet-faktoro, kiu simbolu "nefinion"; * sur nia kampo por tio suficxas eta nombro. */ private final int NEFINIE_DA_FOJOJ = 1025; /** la grandeco de la kampo en bilderoj */ Dimension c; /** la longeco de la kampo en kamperoj */ final int nx = 80; /** la largxeco de la kampo en kamperoj */ final int ny = 24; /** konstanto, kiu simbolas la senmovan atendadon de la fugxanto */ final int ATENDU = KeyEvent.VK_W; /** multobligilo por komandoj */ int oblo = 0; /** cxu la ludanto gajnis? */ private boolean gajnis = false; /** cxu la ludanto malgajnis? */ private boolean malgajnis = false; /** la nombro de la malicaj robotoj */ private final int nrob = 15; /** la robotoj */ protected Roboto rob[] = new Roboto[ nrob ]; /** la ludanto */ protected Fugxanto fugx = null; /** la (akumulitaj) poentoj en la aktuala ludo */ protected int poentoj = 0; /** la gxis nun maksimumaj poentoj */ protected int maksimumajPoentoj = 0; /** la nombro de la robotoj ekzistantaj cxe decido "atendi" */ protected int atendatajRobotoj = 0; /** por vidigi mesagxon */ protected Mesagxisto mesagxisto = null; Kampo() { addKeyListener( this ); valorizu(); } /** * Registru mesagxiston por raporti al la ludanto. * antauxe registritaj mesagixstoj estas forgesitaj. * param m la nova mesagxisto */ void metuMesagxiston( Mesagxisto m ) { mesagxisto = m; } /** * Donu komencajn valorojn al la variabloj. * Hazarde loku la fugxanton * kaj aron da robotoj. * Tamen zorgu, ke la fugxanto ne tuj mortu. */ private void valorizu() { gajnis = false; malgajnis = false; oblo = 0; atendatajRobotoj = 0; for( int nr = 0; nr < nrob; nr++ ) { int x = (int) ( java.lang.Math.random() * nx ); int y = (int) ( java.lang.Math.random() * ny ); rob[ nr ] = new Roboto( x, y ); } fugx = null; while( fugx == null ) { int x = (int) ( java.lang.Math.random() * nx ); int y = (int) ( java.lang.Math.random() * ny ); fugx = new Fugxanto( x, y ); for( int nr = 0; nr < nrob; nr++ ) { if( rob[ nr ].ricevuPozicion().x == x && rob[ nr ].ricevuPozicion().y == y ) { fugx = null; } } } System.gc(); if( mesagxisto != null ) { mesagxisto.mesagxu( "" ); mesagxisto.montruPoentojn( poentoj ); mesagxisto.montruMaksimumajnPoentojn( maksimumajPoentoj ); } requestFocus(); } /** * Pentru la lud-kampon (kradon). */ public void paint( Graphics g ) { if( c == null ) { c = getSize(); } g.setColor( gajnis ? Color.green : malgajnis ? Color.red : Color.black ); for( int ix= 0; ix <= nx; ix++ ) { int x = ix * c.width / nx; g.drawLine( x, 0, x, c.height-1 ); } for( int iy= 0; iy <= ny; iy++ ) { int y = iy * c.height / ny; g.drawLine( 0, y, c.width-1, y ); } for( int nr = 0; nr < rob.length; nr++ ) { rob[nr].pentru( g ); } fugx.pentru( g ); // requestFocus(); } /** * kontrolu, cxu eblas movi la fugxanton al donita kampo */ boolean cxuEblasMovoAl( Point p ) { if( p.x < 0 || p.x >= nx || p.y < 0 || p.y >= ny ) { return false; } for( int nr = 0; nr < rob.length; nr++ ) { if( p.equals( rob[ nr ].ricevuPozicion() ) ) { return false; } } return true; } /** * movu cxiujn pecojn * @param signo la signo, kiu donas la direkton de la movo * @returns cxu eblis la dezirata movo */ boolean faruMovon( int signo ) { if( gajnis || malgajnis ) { return false; } boolean sukceso = true; if( signo != ATENDU && ! (sukceso = fugx.movigxuJe( signo )) ) { return sukceso; // == false } Point pf = fugx.ricevuPozicion(); // movu la robotojn cele al la fugxanto: for( int nr = 0; nr < rob.length; nr++ ) { if( ! rob[ nr ].detruita() ) { rob[nr].movigxuCeleAl( pf ); } } // kontrolu, cxu roboto koliziis kontraux io: for( int nr = 0; nr < rob.length; nr++ ) { for( int nr2 = nr+1; nr2 < rob.length; nr2++ ) { if( rob[ nr ].ricevuPozicion().equals( rob[ nr2 ].ricevuPozicion() ) ) { rob[ nr ] .detruu(); rob[ nr2 ] .detruu(); } } } for( int nr = 0; nr < rob.length; nr++ ) { if( rob[ nr ].ricevuPozicion().equals( fugx.ricevuPozicion())) { malgajnis = true; poentoj = 0; fugx.mortu(); rob[ nr ] .detruu(); System.out.println( "vi malgajnis." ); if( mesagxisto != null ) { mesagxisto.mesagxu( "vi malgajnis." ); mesagxisto.montruPoentojn( poentoj ); } } } int nRestantajRobotoj = nombruRobotojn(); if( nRestantajRobotoj == 0 ) { gajnis = true; System.out.println( "vi gajnis!" ); poentoj += atendatajRobotoj; if( poentoj > maksimumajPoentoj ) { maksimumajPoentoj = poentoj; } if( mesagxisto != null ) { mesagxisto.mesagxu( "vi gajnis!" ); mesagxisto.montruPoentojn( poentoj ); mesagxisto.montruMaksimumajnPoentojn( maksimumajPoentoj ); } } return true; } /** * @returns cxu pozicio p estas dangxera, * do cxu estas roboto najbare. * @param p la ekzamenenda pozicio */ protected boolean dangxera( Point p ) { for( int nr = 0; nr < rob.length; nr++ ) { final Point pr = rob[ nr ].ricevuPozicion(); // ekzamenu, cxu tiu roboto estas najbara: if( ! rob[ nr ].detruita() && Math.abs( pr.x - p.x ) <= 1 && Math.abs( pr.y - p.y ) <= 1 ) { return true; } } return false; } public void keyTyped( KeyEvent e ) { } /** * La uzanto premis klavon. * Lauxe movu la fuxganton */ public void keyPressed( KeyEvent e ) { int komando = e.getKeyCode(); final int d = komando > KeyEvent.VK_9 ? -1 : komando - KeyEvent.VK_0; if( e.getKeyChar() == '.' && komando != KeyEvent.VK_PERIOD ) { // MS IE liveras la kodon 190 por la punkto-klavo System.err.println("KODO: " + komando + " --> " + KeyEvent.VK_PERIOD ); komando = KeyEvent.VK_PERIOD; } switch( komando ) { case KeyEvent.VK_Q: // finu case KeyEvent.VK_R: // rekomencu plenumu( komando ); break; case KeyEvent.VK_W: // atendu atendatajRobotoj = nombruRobotojn(); while( ! gajnis && ! malgajnis ) { faruMovon( ATENDU ); Graphics g = getGraphics(); update( g ); g.dispose(); System.gc(); try { Thread.sleep( 120 ); } catch( InterruptedException esc ) { } } break; case KeyEvent.VK_T: // transportigxu oblo = 0; faruMovon( komando ); repaint(); break; default: if( d >= 0 ) { oblo = 10*oblo + (komando - '0'); } else { final char c = e.getKeyChar(); if( c == 'H' || c == 'J' || c == 'K' || c == 'L' || c == 'Z' || c == 'Y' || c == 'U' || c == 'B' || c == 'N' ) { // majuskloj irigas tiom, kiom eblas: oblo = NEFINIE_DA_FOJOJ; } for( int o = 0; o == 0 || o < oblo; o++ ) { final boolean sukceso = faruMovon( komando ); if( sukceso ) { Graphics g = getGraphics(); update( g ); g.dispose(); try { Thread.sleep( 120 ); } catch( InterruptedException esc ) { } } else { oblo = 0; // finu la ciklon } } oblo = 0; } break; } if( d < 0 ) { oblo = 0; } } public void keyReleased( KeyEvent e ) { } /** * Plenumu donitan komandon. * Gxi povas veni de la klavaro aux de butonpremo. * @param komando la komando */ public void plenumu( int komando ) { switch( komando ) { case KeyEvent.VK_Q: try { System.exit( 0 ); } catch( Exception esc ) { // AppletSecurityException aux io simila } break; case KeyEvent.VK_R: // rekomencu valorizu(); repaint(); break; default: throw( new IllegalArgumentException( "komando " + komando ) ); } } /** * @return la nombron de la ekzistantaj (ne detruitaj) robotoj */ protected int nombruRobotojn() { int nRestantajRobotoj = 0; for( int nr = 0; nr < rob.length; nr++ ) { if( ! rob[ nr ].detruita() ) { nRestantajRobotoj++; } } return nRestantajRobotoj; } } /** * Tiu cxi klaso reprezentas ludpecon. * De gxi derivigxas * Fugxanto * kaj * Roboto. */ abstract class Ulo { /** la pozicio de la ludpeco */ Point poz; /** * Metu novan ludpecon al la donita pozicio * @param x la horizontala koordinato * @param y la vertikala koordinato */ Ulo( int x, int y ) { poz = new Point( x, y ); } /** @returns la pozicion de la ludpeco */ Point ricevuPozicion() { return poz; } /** pentru la ludpecon */ abstract void pentru( Graphics g ); } /** * Tiu cxi klaso reprezentas la robotojn. * Ili povas movigxi ne libere, * sed nur cele al la fugxanto. */ class Roboto extends Ulo { /** cxu la roboto jam estis detruita? */ protected boolean detruita = false; /** Metu novan roboton sur la kampon */ Roboto( int x, int y ) { super( x, y ); } /** * Pentru la roboton. * Depende de la detruiteco gxi estas flava aux rugxa. */ void pentru( Graphics g ) { int x = poz.x * kampo.c.width / kampo.nx; int y = poz.y * kampo.c.height / kampo.ny; g.setColor( detruita ? Color.yellow : Color.red ); g.fillOval( x, y, kampo.c.width / kampo.nx, kampo.c.height / kampo.ny ); } /** * Movu la roboton direkte al donita punkto. * @param celo la cel-punkto */ void movigxuCeleAl( Point celo ) { if( ! detruita ) { int dx = celo.x - poz.x; int dy = celo.y - poz.y; dx = dx > 0 ? 1 : dx < 0 ? -1 : 0; dy = dy > 0 ? 1 : dy < 0 ? -1 : 0; poz.x += dx; poz.y += dy; } } /** * Diru al la roboto, ke gxi jxus estis detruita. * Riparo ne eblas. */ void detruu() { detruita = true; } /** * @returns cxu la roboto estas detruita */ boolean detruita() { return detruita; } } /** * La klaso por la fugxanto. * Gxi havas komunan superklason kun siaj malamikoj, la robotoj. */ class Fugxanto extends Ulo { /** cxu la fugxanto mortis? */ protected boolean mortinta = false; /** Metu novan fugxanton sur la kampon. */ Fugxanto( int x, int y ) { super( x, y ); } /** pentru la ulon kiel ovalon */ void pentru( Graphics g ) { int x = poz.x * kampo.c.width / kampo.nx; int y = poz.y * kampo.c.height / kampo.ny; g.setColor( mortinta ? Color.orange : Color.blue ); g.fillOval( x, y, kampo.c.width / kampo.nx, kampo.c.height / kampo.ny ); } /** necesas morti... */ void mortu() { mortinta = true; } /** movu la donitan punkton laux la donita direkto. Direkto povas esti: @return la novan pozicion */ Point movuJe( int direkto ) { Point p = new Point( poz ); switch( direkto ) { case KeyEvent.VK_Y: case KeyEvent.VK_Z: // por Germanaj klavaroj case KeyEvent.VK_NUMPAD7: p.x -= 1; p.y -= 1; break; case KeyEvent.VK_K: case KeyEvent.VK_NUMPAD8: case KeyEvent.VK_UP: p.y -= 1; break; case KeyEvent.VK_U: case KeyEvent.VK_NUMPAD9: p.x += 1; p.y -= 1; break; case KeyEvent.VK_H: case KeyEvent.VK_NUMPAD4: case KeyEvent.VK_LEFT: p.x -= 1; break; case KeyEvent.VK_NUMPAD5: case KeyEvent.VK_PERIOD: case KeyEvent.VK_COMMA: break; case KeyEvent.VK_L: case KeyEvent.VK_NUMPAD6: case KeyEvent.VK_RIGHT: p.x += 1; break; case KeyEvent.VK_B: case KeyEvent.VK_NUMPAD1: p.x -= 1; p.y += 1; break; case KeyEvent.VK_J: case KeyEvent.VK_NUMPAD2: case KeyEvent.VK_DOWN: p.y += 1; break; case KeyEvent.VK_N: case KeyEvent.VK_NUMPAD3: p.x += 1; p.y += 1; break; default: p = null; break; } return p; } /** movu la fugxanton laux la donita direkto. Pri la difino de direktoj konsultu la metodon movuJe. * @return cxu eblas tiu movo */ boolean movigxuJe( int direkto ) { boolean sukceso = true; final Point malnovaPozicio = new Point( poz ); switch( direkto ) { case KeyEvent.VK_T: // saltu saltu(); break; default: poz = movuJe( direkto ); if( poz == null ) { System.err.println( "klavo: " + direkto ); sukceso = false; } else if( kampo.dangxera( poz ) ) { sukceso = false; } break; } if( poz != null && ! kampo.cxuEblasMovoAl( poz ) ) { sukceso = false; } if( ! sukceso ) { poz = malnovaPozicio; } return sukceso; } /** * saltu al hazarda pozicio */ protected void saltu() { int x = -1, y = -1; do { x = (int) ( Math.random() * kampo.nx ); y = (int) ( Math.random() * kampo.ny ); } while( Math.abs( x - poz.x ) < 4 && Math.abs( y - poz.y ) < 4 ); poz.x = x; poz.y = y; } } /** * Tio cxi estas la buton-panelo, * kiu ankaux montras la datenojn (poentojn). */ class Butonaro extends Panel implements ActionListener, Mesagxisto { /** la auxskultanto, al kiu sendi niajn ordonojn */ Komandato auxskultanto = null; /** la butono por rekomenci */ Button buRekomencu = null; /** la butono por fini */ Button buFinu = null; /** tekst-linio por mesagxetoj */ Label teksto; /** tekst-linio por poentoj */ Label poentoj; /** tekst-linio por la maksimuma nombro de la poentoj */ Label maksimumajPoentoj; /** * Konstruu novan butonaron. * En krozilo ne utilas havi butonon por fini. * @param a destinloko por la komandoj * @param havuButononFinu cxu la butonaro havu butonon por fini * la programon? */ Butonaro( Komandato a, boolean havuButononFinu ) { auxskultanto = a; setLayout( new FlowLayout() ); buRekomencu = new Button( "rekomencu" ); buRekomencu.addActionListener( this ); add( buRekomencu ); if( havuButononFinu ) { buFinu = new Button( "finu" ); buFinu .addActionListener( this ); add( buFinu ); } teksto = new Label( " " ); add( teksto ); poentoj = new Label( " " ); add( poentoj ); maksimumajPoentoj = new Label( " " ); add( maksimumajPoentoj ); } /** okazis evento (butono premita) */ public void actionPerformed( ActionEvent e ) { Object fonto = e.getSource(); if( fonto == buRekomencu ) { auxskultanto.plenumu( KeyEvent.VK_R ); } if( fonto == buFinu ) { auxskultanto.plenumu( KeyEvent.VK_Q ); } } /** cxiuj idoj sendu klav-premojn al la auxskultanto */ public void addKeyListener( KeyListener k ) { super .addKeyListener( k ); buRekomencu.addKeyListener( k ); if( buFinu != null ) { buFinu .addKeyListener( k ); } } /** vidigu etan mesagxon */ public void mesagxu( String t ) { teksto.setText( t ); } /** vidigu poentojn */ public void montruPoentojn( int po ) { poentoj.setText( "" + po ); System.out.println("poentoj: " + po); } /** vidigu la gxis nun maksimumajn poentojn */ public void montruMaksimumajnPoentojn( int mpo ) { maksimumajPoentoj.setText( "" + mpo ); } } }