import java.util.ArrayList; /** * @author Justin Hogg * * @date 7/11/2006 * @version 0.1 * * This class receives basic game events * from the GameAnalyser class, makes appropriate calls * to the CommentTemplateStore to obtain a suitable comment * for a given event and then adds the comment to the proposistion * pool in the CommentScheduler. * * This class also eliminates duplication of free kick, keeping possession * and winning possesion comments for the SAME event * */ public class CommentaryProducer { private CommentTemplateStore cts; private CommentScheduler cs; private Stats stats; private static long MIN_INTERVAL = 2000; // Minimum interval between any two repating comments // these variables are used to eliminate repitition of comments // for events that are repeatedly called (for the SAME occurance) by GA private long lastFKTime = 0; // time of last SPOKEN free kick Comment private long lastKPTime = 0; // time of last SPOKEN team keeping possession Comment private long lastWPTime = 0; // time of last SPOKEN wins possesion comment private long lastOTTime = 0; // time of last SPOKEN one-two comment private long lastPLTime = 0; // time of last SPOKEN possesion lost comment // Constructor public CommentaryProducer(Output commentator, Stats stats) { cts = new CommentTemplateStore(); cs = new CommentScheduler(commentator); //new Thread(cs).start(); cs.start(); this.stats = stats; } /** * Method that receives match period (half-time, full-time, etc) * for processing from GameAnalyser class * * @param time current period of the game * */ public void speakMatchTime(int time) { if(time == 1500) { Comment tc = cts.getTimeComment(1); cs.addToPool(tc.getImportance(), tc.getPartOne() ); } if(time == 2700) { Comment tc = cts.getTimeComment(2); cs.addToPool(tc.getImportance(), tc.getPartOne() ); } if(time == 2999) { Comment tc = cts.getTimeComment(3); cs.addToPool(tc.getImportance(), tc.getPartOne() ); } if(time == 4500) { Comment tc = cts.getTimeComment(4); cs.addToPool(tc.getImportance(), tc.getPartOne() ); } if(time == 5700) { Comment tc = cts.getTimeComment(5); cs.addToPool(tc.getImportance(), tc.getPartOne() ); } } /** * Method that receives a statistical comment (string) * from GA for processing at 500ms game intervals * * Has a high priority - will not be spoken if a goal, * shot or missed shot comment is available but will be in * all other instances * * @param stat String statistical string providing game information * */ public void speakStat(String stat) { cs.addToPool(4, stat); } /** * Method that receives details of out of bounds kick * (crossing vertical goaline) * for processing from GameAnalyser class * * @param keeper true if the ball deflected out of bounds off of the goalkeeper * */ public void speakVerticalOut(boolean keeper) { Comment voc = cts.getVOComment(keeper); cs.addToPool(voc.getImportance(), voc.getPartOne() ); } /** * Method that receives details of kick in * (ball crossing horizontal boundary line) * for processing from GameAnalyser class * */ public void speakHorizontalOut() { Comment hoc = cts.getHOComment(); cs.addToPool(hoc.getImportance(), hoc.getPartOne() ); } /** * Method that receives details of a free kick * for processing from GameAnalyser class * * @param team String team awarded the free kick * */ public void speakFreeKick(String team) { if( overMinimumInterval(lastFKTime) ) { Comment fkc = cts.getFreeKickComment(); cs.addToPool(fkc.getImportance(), fkc.getPartOne() + team ); lastFKTime = System.currentTimeMillis(); } } /** * Method that receives details of player offside * for processing from GameAnalyser class * */ public void speakOffside() { Comment osc = cts.getOffsideComment(); cs.addToPool(osc.getImportance(), osc.getPartOne() ); } /** * Method that receives details of attack on defenders * for processing from GameAnalyser class * */ public void speakDefenderPressure() { Comment dpc = cts.getPressureDefenceComment(); cs.addToPool(dpc.getImportance(), dpc.getPartOne() ); } /** * Method that receives details of player dribbling ball * (defined in GA by same player touching ball 5 times) * for processing from GameAnalyser class * */ public void speakPlayerDribbles(int player) { Comment drib = cts.getDribblingComment(); String completeComment = markupRawPlayer(player) + drib.getPartOne(); cs.addToPool(drib.getImportance(), completeComment); } /** * Method that receives details of one-two between two players * for processing from GameAnalyser class * */ public void speakOneTwo(int playerOne, int playerTwo) { if( overMinimumInterval(lastOTTime) ) { Comment oneTwo = cts.getOneTwoComment(); String completeComment = oneTwo.getPartOne() + markupRawPlayer(playerOne) + oneTwo.getPartTwo() + markupRawPlayer(playerTwo); cs.addToPool(oneTwo.getImportance(), completeComment); lastOTTime = System.currentTimeMillis(); } } /** * Method that receives details of player winning back possession * for processing from GameAnalyser class * */ public void speakWinsPossession(int player) { if( overMinimumInterval(lastWPTime) ) { Comment wp = cts.getWinsPossessionComment(); String completeComment = markupRawPlayer(player) + wp.getPartOne(); cs.addToPool(wp.getImportance(), completeComment); lastWPTime = System.currentTimeMillis(); } } /** * Method that receives details of team keeping possession well * for processing from GameAnalyser class * */ public void speakTeamKeepingPossession(String team) { if( overMinimumInterval(lastKPTime) ) { Comment tkp = cts.getKeepingPossessionComment(); String completeComment = team + tkp.getPartOne(); cs.addToPool(tkp.getImportance(), completeComment); lastKPTime = System.currentTimeMillis(); } } /** * Method that receives goal data for processing * from GameAnalyser class * * @param touch true indicates Goalie touched but couldn't save * @param distance distance in yards from which the goal was scored * @param player int player number that scored * @param goals int number of goal scored by player * */ public void speakGoal(boolean touch, int distance, int player, int goals) { if(touch == true) { Comment tgc = cts.getTouchedGoalComment(); String completeComment = markupRawPlayer(player) + tgc.getPartOne() + goals + tgc.getPartTwo() + distance + tgc.getPartThree(); cs.addToPool(tgc.getImportance(), completeComment); } else { Comment ugc = cts.getUntouchedGoalComment(); String completeComment = markupRawPlayer(player) + ugc.getPartOne() + goals + ugc.getPartTwo() + distance + ugc.getPartThree() ; cs.addToPool(ugc.getImportance(), completeComment); } } /** * Method that receives unsuccessful goal shot for processing * from GameAnalyser class * * @param playerNum int The number of the player who took the shot * @param distance int distance of the shot * */ public void speakShotMissed(int playerNum, int distance) { Comment ms = cts.getMissedShotComment(); if (distance > 0) { String completeComment = ms.getPartOne() + distance + ms.getPartTwo() + markupRawPlayer(playerNum); cs.addToPool(ms.getImportance(), completeComment); } else { String hitsPost = "OOOHHHHHHHHHHHH, Shot from " + markupRawPlayer(playerNum) + " hits post"; cs.addToPool(ms.getImportance(), hitsPost); } } /** * Method that receives indication that game is starting * from GameAnalyser class * * @param teamL String name of team on the left side of the pitch * @param teamR String name of team on the right side of the pitch * */ public void gameStart(String teamL, String teamR) { Comment matchStart = cts.getMatchStartComment(); String completeComment = matchStart.getPartOne() + teamL + matchStart.getPartTwo() + teamR + matchStart.getPartThree(); cs.addToPool(matchStart.getImportance(), completeComment); } /** * Method that receives indication that game is starting * from GameAnalyser class, and includes details from previous * match (if applicable) involving both teams * * @param teamL String name of team on the left side of the pitch * @param teamR String name of team on the right side of the pitch * @param winTeam String name of winning team from last match of teamL and TeamR * @param margin Integer win margin from previous match * */ public void gameStart(String teamL, String teamR, String winTeam, int margin) { Comment matchStart = cts.getMatchStartComment(); String completeComment = matchStart.getPartOne() + teamL + matchStart.getPartTwo() + teamR + matchStart.getPartThree(); if (margin < 0) completeComment += " These teams have not met before"; else if (margin == 0) completeComment += " These two teams drew last time they met"; else completeComment += winTeam + " won by " + margin + " goals last time these teams met"; cs.addToPool(matchStart.getImportance(), completeComment); } /** * Method that announces game draw at full time * *@param manOfMatch int - player who scored the most goals */ public void speakDraw(int manOfMatch) { Comment dc = cts.getMatchDrawComment(); String completeComment = dc.getPartOne() + markupRawPlayer(manOfMatch) + dc.getPartTwo(); cs.addToPool(dc.getImportance(), completeComment ); } /** * Method that announces winning team and winning goal margin * *@param winTeam String - name of winning team *@param margin int - goal margin of winning team *@param manOfMatch - int player who scored the most goals */ public void speakWin(String winTeam, int margin, int manOfMatch) { Comment win = cts.getMatchWinComment(); String completeComment = win.getPartOne() + winTeam + win.getPartTwo() + margin + win.getPartThree() + markupRawPlayer(manOfMatch) + win.getPartFour(); cs.addToPool(win.getImportance(), completeComment); } /** * Method that speaks details of a pass * also merges string template parts and variables to produce * Comment object containing one string for commentary * * @param passer Number of the player who passed * @param receiver Number of the player receiving the ball * @param distance distance of the pass * @param position position of ball on pitch as String ("left", "right", "center") */ public void speakPass(int passer , int receiver, int distance, String position) { Comment pass = cts.getPassComment(); // get template String completeComment; // now mark up template strings with variables if (distance < 25) completeComment = markupRawPlayer(receiver) + pass.getPartOne() + markupRawPlayer(passer); else completeComment = markupRawPlayer(receiver) + pass.getPartOne() + markupRawPlayer(passer) + pass.getPartTwo(); cs.addToPool(pass.getImportance(), completeComment); } /** * Method that announces details of shot * * @param passer Number of the player who passed * @param distance int distance from goal of shot */ public void speakShot(int player, int distance) { Comment shot = cts.getShotComment(); String completeComment = markupRawPlayer(player) + shot.getPartOne() + distance + shot.getPartTwo(); cs.addToPool(shot.getImportance(), completeComment); } /** * Method that announces goal kick to opposition * */ public void speakBadGoalKick() { Comment bgk = cts.getBadGoalKickComment(); cs.addToPool(bgk.getImportance(), bgk.getPartOne() ); } /** * Method that announces possession transfering to opposition * also merges string template parts and variables to produce * Comment object containing one string for commentary * * @param loser Number of the player who lost ball/possession */ public void speakPossessionLost(int loser) { if( overMinimumInterval(lastPLTime) ) { Comment pl = cts.getPossessionLostComment(); // get template // now mark up template string with variables String completeComment = markupRawPlayer(loser) + pl.getPartOne(); cs.addToPool(pl.getImportance(), completeComment); lastPLTime = System.currentTimeMillis(); } } /** * Method that announces counter attack * (receives data from veroni class via GA) * * Suggests team is in strong position for attack on goal * * @param team team that has the chance of counter attack * */ public void counterAttack(String team) { Comment ca = cts.getCounterAttackComment(); // get template // now markup template with variables String completeComment = team + ca.getPartOne(); cs.addToPool(ca.getImportance(), completeComment); } /** * Method that announces open goal * (receives data from veroni class via GA) * * Suggests goal is open * */ public void openGoal() { Comment ogc = cts.getOpenGoalComment(); cs.addToPool(ogc.getImportance(), ogc.getPartOne() ); } /** * Method that announces that a cross would put * team in good position for a goal * (receives data from veroni class via GA) * * * @param team the team that should cross the ball * */ public void crossChance(String team) { Comment cc = cts.getCrossChanceComment(); //get template // markup with variables String completeComment = team + cc.getPartOne(); cs.addToPool(cc.getImportance(), completeComment); } /** * Method that announces last defender has been passed * i.e. just attacker and goalkeeper now * (receives data from veroni class via GA) * * @param team the team that should cross the ball * */ public void pastLastDefender(int player) { Comment pld = cts.getPastLastDefenderComment(); // get template // markup with variables String completeComment = pld.getPartOne() + markupRawPlayer(player); cs.addToPool(pld.getImportance(), completeComment); } /** * Method that returns true if time interval between * two speak method calls is greater than the * set minimum (determined by MIN_INTERVAL) * * Used to prevent duplication of comments for * GA calls that can make multiple calls to a speak method * for the same occurance of that event * * used for : speakFreeKick(), speakTeamkeepingPossesion(), * speakWinsPossesion(), speakOneTwo() and * * @param lastoccuranceTime long - for the given event * @returns true if event occured longer than MIN_INTERVAL ago, false otherwise */ private boolean overMinimumInterval(long lastOccuranceTime) { return (System.currentTimeMillis() - lastOccuranceTime > MIN_INTERVAL); } /** * Method that takes raw player number (1-22) from GameAnalyzer * and converts to team colour plus team number: * * 3 becomes "blue 3" (players 1-11 are on the left - the blue team) * 12 becomes "red 1" (players 12-22 are on the right - the red team) * i.e. players 12-22 are players 1-11 on red team * * @param player the raw player data 1-22 - Assumes no subs such as 13 or 23 * @return the marked-up player data as a String i.e. "red 1" * */ private String markupRawPlayer(int rawPlayer) { if(rawPlayer < 12) return "Blue " + rawPlayer; else return "Red " + (rawPlayer - 11); } }