package apps.kiosk; import java.awt.Color; import java.awt.Graphics; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.awt.event.MouseMotionAdapter; import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.regex.Matcher; import java.util.regex.Pattern; import javax.swing.JPanel; import util.gdl.factory.GdlFactory; import util.gdl.grammar.GdlSentence; import util.observer.Event; import util.observer.Observer; import util.observer.Subject; import util.statemachine.MachineState; import util.statemachine.Move; import util.statemachine.Role; import util.statemachine.StateMachine; import util.statemachine.exceptions.MoveDefinitionException; public abstract class GameCanvas extends JPanel implements Subject { public static final long serialVersionUID = 0x1; // Store the information about the current state of the game protected StateMachine stateMachine; protected MachineState gameState; protected Role myRole; // Cache the location of the last click private int lastClickX; private int lastClickY; // Border constant private static final int BORDER_SIZE = 10; public GameCanvas() { super(); setFocusable(true); // Fiddle with Mouse Settings addMouseListener( new MouseAdapter() { public void mousePressed(MouseEvent e) { requestFocusInWindow(); // Update the click point cache lastClickX = e.getX(); lastClickY = e.getY(); handleClickEventWrapper(lastClickX, lastClickY); repaint(); } public void mouseReleased(MouseEvent e){ lastClickX = -1; lastClickY = -1; } }); addMouseMotionListener( new MouseMotionAdapter() { public void mouseDragged(MouseEvent e) { if(lastClickX == -1) return; // compute delta from last point int dx = e.getX() - lastClickX; int dy = e.getY() - lastClickY; handleDragEventWrapper(dx, dy); // Update the click point cache lastClickX = e.getX(); lastClickY = e.getY(); } }); } public void setStateMachine(StateMachine s) { stateMachine = s; } public void setRole(Role r) { myRole = r; } public void paintComponent(Graphics g){ super.paintComponent(g); g.setColor(Color.BLACK); g.drawRect(4, 4, getWidth() - 8, getHeight() - 8); g.drawRect(6, 6, getWidth() - 12, getHeight() - 12); if(!isEnabled()) { g.setColor(Color.red); g.drawRect(5, 5, getWidth() - 10, getHeight() - 10); } Graphics newG = g.create(BORDER_SIZE, BORDER_SIZE, getWidth() - 2*BORDER_SIZE, getHeight() - 2*BORDER_SIZE); if(gameState != null) { paintGame(newG); } else { paintGameDefault(newG, "Waiting for game state..."); } } // Subject boilerplate private Set<Observer> theObservers = new HashSet<Observer>(); @Override public void addObserver(Observer observer) { theObservers.add(observer); } @Override public void notifyObservers(Event event) { for(Observer theObserver : theObservers) theObserver.observe(event); } protected void submitWorkingMove(Move theMove) { notifyObservers(new MoveSelectedEvent(theMove)); } protected void submitFinalMove(Move theMove) { notifyObservers(new MoveSelectedEvent(theMove, true)); } public void updateGameState(MachineState gameState) { this.gameState = gameState; clearMoveSelection(); try { List<Move> legalMoves = stateMachine.getLegalMoves(gameState, myRole); if(legalMoves.size() > 1) { submitWorkingMove(null); } else { //submitWorkingMove(legalMoves.get(0)); submitFinalMove(legalMoves.get(0)); } } catch (MoveDefinitionException e) { submitWorkingMove(null); } } private void paintGameDefault(Graphics g, String message) { int width = g.getClipBounds().width; int height = g.getClipBounds().height; g.setColor(Color.WHITE); g.fillRect(0, 0, width, height); g.setColor(Color.BLACK); g.drawString(message, width/2 - message.length()*2, height/2); } /* ---------- Convenience methods ---------- */ protected boolean gameStateHasFact(String fact) { String trueFact = "( true " + fact + " )"; for(GdlSentence aFact : gameState.getContents()) { if(aFact.toString().equalsIgnoreCase(trueFact)) return true; } return false; } protected Set<String> gameStateHasFactsMatching(String ptrn) { Pattern pattern = Pattern.compile("\\( true " + ptrn + " \\)"); Set<String> theMatches = new HashSet<String>(); for(GdlSentence theFact : gameState.getContents()) { Matcher matcher = pattern.matcher(theFact.toString()); if (matcher.find()) { String match = matcher.group(); match = match.substring(7, match.length() - 2); theMatches.add(match); } } return theMatches; } protected boolean gameStateHasLegalMove(String move) { try { List<Move> legalMoves = stateMachine.getLegalMoves(gameState, myRole); for(Move aMove : legalMoves) { if(aMove.toString().equalsIgnoreCase(move)) return true; } return false; } catch(Exception e) { return false; } } protected Set<String> gameStateHasLegalMovesMatching(String ptrn) { Pattern pattern = Pattern.compile(ptrn); Set<String> theMatches = new HashSet<String>(); try { List<Move> legalMoves = stateMachine.getLegalMoves(gameState, myRole); for(Move theMove : legalMoves) { Matcher matcher = pattern.matcher(theMove.toString()); if (matcher.find()) { String match = matcher.group(); theMatches.add(match); } } return theMatches; } catch (MoveDefinitionException e) { // This can happen in games where the terminal state // has no legal moves defined for a player. //e.printStackTrace(); return new HashSet<String>(); } } protected Move stringToMove(String move) { try { return stateMachine.getMoveFromSentence((GdlSentence)GdlFactory.create(move)); } catch(Exception e) { return null; } } /* ---------- Enabling wrappers ------------ */ private void handleDragEventWrapper(int dx, int dy) { if(!isEnabled()) return; handleDragEvent(dx, dy); } private void handleClickEventWrapper(int x, int y) { if(!isEnabled()) return; handleClickEvent(x, y); } /* ---------- For overriding! -------------- */ public abstract String getGameName(); protected abstract String getGameKey(); protected void paintGame(Graphics g) { paintGameDefault(g, "paintGame not implemented"); } protected void handleDragEvent(int dx, int dy) { ; } protected void handleClickEvent(int x, int y) { ; } public abstract void clearMoveSelection(); }