package playball.gameEngine;
import playball.gameEngine.containers.Pitcher;
import playball.gameEngine.containers.Batter;
import playball.gameEngine.containers.Team;
/**
* The Game class initializes a baseball game between two specified teams and
* provides the methods to simulate the game.
*
* @author
* Sam Jones (samjones1986@gmail.com)
**/
public class Game
{
/** the visiting team **/
private Team visitor;
/** the home team **/
private Team home;
/** the number of runs scored by the visiting team */
private int visitorScore;
/** the number of runs scored by the home team */
private int homeScore;
/** the spot in the order of the next batter from the visiting team **/
private int visitorOrderSpot;
/** the spot in the order of the next batter from the home team **/
private int homeOrderSpot;
/** the current inning **/
private int inning;
/** the current inning half (0 - away, 1 - home) */
private int inningHalf;
/** the number of outs in the current inning */
private int outs;
/** the bases of the diamond, where index 0 is the batter at the plate**/
private Batter[] bases;
private final double PROB_DP = 0.4466;
/**
* Creates a new instance of Game with the specified home and visiting
* teams and the game ID.
*
* @param id
* the ID number of the game, in Retrosheet format
* @param v
* the Team object representing the visiting team
* @param h
* the Team object representing the home team
**/
public Game(String id, Team v, Team h)
{
visitor = v;
home = h;
visitorScore = 0;
homeScore = 0;
visitorOrderSpot = 0;
homeOrderSpot = 0;
inning = 0;
//playByPlay = new Scoresheet(id, v, h);
}
/**
* Plays a baseball game between the two teams.
*
* @return
* the score of the game, where [0] is the visiting team's score and [1]
* is the home team's score
*/
public int[] playGame()
{
System.out.println("Playing game");
//loops for each inning of the game
//normally stops after the 9th inning, but continues if a tie game
for (inning = 1; inning <= 9 || scoreIsTied(); inning++)
{
playHalfInning(0);
//used for the bottom of the inning
//if the inning is 9th or later, checks to see if the score is tied
//or the home team is losing
if (inning < 9 || homeIsLosing() || scoreIsTied())
playHalfInning(1);
}
int score[] = {visitorScore, homeScore};
return score;
}
/**
* Gets the game's visiting team.
*
* @return
* a Team object representing the visiting team
**/
public Team getVisitorTeam()
{
return visitor;
}
/**
* Gets the game's home team.
*
* @return
* a Team object representing the home team
**/
public Team getHomeTeam()
{
return home;
}
/**
* Gets the visiting team's runs.
*
* @return
* the visiting team's score for the game
**/
public int getVisitorScore()
{
return visitorScore;
}
/**
* Gets the home team's runs.
*
* @return
* the home team's score for the game
**/
public int getHomeScore()
{
return homeScore;
}
/**
* Gets the current inning.
*
* @return
* the current inning of the game
**/
public int getInning()
{
return inning;
}
/**
* Determine whether the game is tied or not.
*
* @return
* true if the game is tied, false otherwise
**/
private boolean scoreIsTied()
{
return (visitorScore == homeScore);
}
/**
* Determine whether the home team is losing.
*
* @return
* true if the home team is losing, false otherwise
**/
private boolean homeIsLosing()
{
return (visitorScore > homeScore);
}
/**
* Play half of an inning.
*
* @param half
* the half of the inning to play; 0 for top, 1 for bottom
* @return
* the number of runs scored in the half inning
**/
private void playHalfInning(int half)
{
outs = 0;
inningHalf = half;
bases = new Batter[4];
int outcome; //outcome of an at bat
char ballType = ' '; //the batted ball type (for an out)
//only executes if there are less than 3 outs, and the visiting team is
//batting, or if the home team still has the opportunity to bat, such as
//before the 9th inning, or if the home team is losing or the game is
//tied in the 9th inning or later
while (outs < 3 && continueInning())
{
System.out.println("Inn:" + inning + " Half:" + half +
" Outs:" + getOuts() +
" Home:" + homeScore + " Vis:" + visitorScore);
System.out.println("1st: " + getRunnerOnFirst());
System.out.println("2nd: " + getRunnerOnSecond());
System.out.println("3rd: " + getRunnerOnThird());
if (willBeRunning(1))
{
System.out.println(getRunnerOnFirst() +
" attempts to steal 2nd");
attemptSteal(1);
if (getRunnerOnSecond() == null)
System.out.println("Steal unsuccessful");
else
System.out.println("Steal successful");
System.out.println("--------------------------------------");
}
else
{
Batter nextBatter;
Pitcher thePitcher;
if (half == 0)
{
nextBatter = nextVisitorBatter();
thePitcher = (Pitcher) home.getFielder(1);
}
else
{
nextBatter = nextHomeBatter();
thePitcher = (Pitcher) visitor.getFielder(1);
}
//AtBat matchup = new AtBat(nextBatter, thePitcher);
AtBat matchup = new AtBat(nextBatter,thePitcher);
sendBatterUp(nextBatter);
matchup.generateAtBat();
outcome = matchup.getOutcome();
if (outcome == 2)
ballType = matchup.getBattedBallType();
else
ballType = ' ';
System.out.println(nextBatter + " hits a " +
outcome + "/" + ballType +
" off of " + thePitcher);
advanceRunners(outcome, ballType);
System.out.println("--------------------------------------");
}
}
}
/**
* Retrieves the next batter on the visiting team.
*
* @return
* the batter on the visiting team due up next
*/
private Batter nextVisitorBatter()
{
visitorOrderSpot++;
if (visitorOrderSpot > 9)
visitorOrderSpot = 1;
return visitor.getBatter(visitorOrderSpot);
}
/**
* Retrieves the next batter on the home team.
*
* @return
* the batter on the home team due up next
*/
private Batter nextHomeBatter()
{
homeOrderSpot++;
if (homeOrderSpot > 9)
homeOrderSpot = 1;
return home.getBatter(homeOrderSpot);
}
/**
* Gets the batter currently up to bat.
* @return
* the batter at the plate
*/
public Batter getBatterUp()
{
return bases[0];
}
/**
* Gets the runner currently on first base.
* @return
* the runner on first
*/
public Batter getRunnerOnFirst()
{
return bases[1];
}
/**
* Gets the runner currently on second base.
* @return
* the runner on second
*/
public Batter getRunnerOnSecond()
{
return bases[2];
}
/**
* Gets the runner currently on third base.
* @return
* the runner on third
*/
public Batter getRunnerOnThird()
{
return bases[3];
}
/**
* Gets the number of outs in the current inning.
* @return
* the current number of outs
*/
public int getOuts()
{
return outs;
}
/**
* Places the specified batter at the plate to be ready for the outcome of
* an at bat.
* @param b
* the Batter to send to the plate
*/
public void sendBatterUp(Batter b)
{
bases[0] = b;
}
/**
* Advance the runners on the diamond according to the outcome of an at bat
* and the type of the batted ball, if any. Returns the number of runs
* scored on the play.
*
* @param action
* the integer representation of the outcome of an at bat:
* @param battedBallType
* the type of the batted ball, mostly used for outs
* 'F' - fly ball
* 'L' - line drive
* 'P' - pop up
* 'G' - ground ball
* ' ' - no batted ball or no type specified
* @throws IllegalArgumentException
* indicates that action has been passed an illegal value
**/
public void advanceRunners(int action, char battedBallType)
{
if (action >= 14 && action <= 16) //batter walks or is hit by pitch
{
if (bases[1] != null)
{
if (bases[2] != null)
{
if (bases [3] != null)
{
this.scoreRun();
bases[3] = null;
}
bases[3] = bases[2];
bases[2] = null;
}
bases[2] = bases[1];
bases[1] = null;
}
bases[1] = bases[0];
bases[0] = null;
}
else if (action == 20) //batter singles
{
if (bases[3] != null && continueInning())
{
this.scoreRun();
bases[3] = null;
}
if (bases[2] != null && continueInning())
{
this.scoreRun();
bases[2] = null;
}
if (bases[1] != null)
{
bases[2] = bases[1];
bases[1] = null;
}
bases[1] = bases[0];
bases[0] = null;
}
else if (action == 21) //batter doubles
{
if(bases[3] != null && continueInning())
{
this.scoreRun();
bases[3] = null;
}
if(bases[2] != null && continueInning())
{
this.scoreRun();
bases[2] = null;
}
if(bases[1] != null)
{
bases[3] = bases[1];
bases[1] = null;
}
bases[2] = bases[0];
bases[0] = null;
}
else if (action == 22) //batter triples
{
if(bases[3] != null && continueInning())
{
this.scoreRun();
bases[3] = null;
}
if(bases[2] != null && continueInning())
{
this.scoreRun();
bases[2] = null;
}
if(bases[1] != null && continueInning())
{
this.scoreRun();
bases[1] = null;
}
bases[3] = bases[0];
bases[0] = null;
}
else if (action == 23) //batter homers
{
if(bases[3] != null)
{
this.scoreRun();
bases[3] = null;
}
if(bases[2] != null)
{
this.scoreRun();
bases[2] = null;
}
if(bases[1] != null)
{
this.scoreRun();
bases[1] = null;
}
this.scoreRun();
bases[0] = null;
}
else if (action == 3) //batter strikes out
{
bases[0] = null;
outs++;
}
else if (action == 2 && battedBallType == 'F') //batter flies out
{
if (outs < 2 && bases[3] != null) //possbile sacrifice fly
{
bases[3] = null;
this.scoreRun();
}
bases[0] = null;
outs++;
}
else if (action == 2 && battedBallType == 'G') //batter grounds out
{
if (bases[1] == null && bases[2] == null && bases[3] == null)
{
bases[0] = null;
outs++;
}
else if (bases[1] != null) //possible double play
{
if (bases[2] != null)
{
if (bases[3] != null) //bases loaded
{
bases[3] = bases[2];
bases[2] = bases[1];
bases[1] = bases[0];
bases[0] = null;
outs++;
}
else //runners on 1st and 2nd
{
if (Math.random() < PROB_DP) //a double play is turned
{
bases[3] = bases[2];
bases[2] = null;
bases[1] = null;
bases[0] = null;
outs++;
if (outs < 3)
outs++;
}
else //double play is unsuccessful
{
bases[3] = bases[2];
bases[2] = null;
bases[1] = bases[0];
bases[0] = null;
outs++;
}
}
}
else //runner on 1st or runners on 1st and 3rd
{
if (Math.random() < PROB_DP) //a double play is turned
{
bases[2] = null;
bases[1] = null;
bases[0] = null;
outs++;
if (outs < 3)
outs++;
}
else //double play is unsuccessful
{
bases[2] = null;
bases[1] = bases[0];
bases[0] = null;
outs++;
}
}
}
else //no runner on 1st, but not bases empty
{
bases[0] = null;
outs++;
}
}
else if (action == 2) //batter is out some other way
{
bases[0] = null;
outs++;
}
else //action has been passed an illegal value
{
String msg = "action " + action + " is an illegal argument";
throw new IllegalArgumentException(msg);
}
}
/**
* Determines whether to continute the inning or not.
* @return
* true if the inning should continue, false otherwise
*/
private boolean continueInning()
{
return inningHalf == 0 || inning < 9 || visitorScore >= homeScore;
}
/**
* Makes the runner on the specified base attempt to steal.
*
* @param runner
* the base the runner that would like to steal is on
*/
public void attemptSteal(int runner)
{
int baseToSteal = runner + 1;
if ((runner == 1 || runner == 2) &&
bases[runner] != null && bases[baseToSteal] == null)
{
if (Math.random() < (bases[runner].getStolenBaseSuccessRate()))
{
bases[baseToSteal] = bases[runner];
bases[runner] = null;
}
else
{
bases[runner] = null;
outs++;
}
}
}
/**
* Determines whether or not the runner on the specified base will be
* running.
*
* @param runner
* the base that is occupied by the potential base stealer
* @return
* true if the runner will attempt to steal, false if not
*/
public boolean willBeRunning(int runner)
{
int baseToSteal = runner + 1;
if ((runner == 1 || runner == 2) &&
bases[runner] != null && bases[baseToSteal] == null)
{
return Math.random() < bases[runner].getStolenBaseAttemptRate();
}
else
{
return false;
}
}
/**
* Tally a run scored for the current batting team.
*/
private void scoreRun() {
if (inningHalf == 0) {
this.visitorScore++;
}
else {
this.homeScore++;
}
}
}