Refactor game logic and improve state management, add some tests
- Added `handleHit` function to handle hit types (single, double, triple, home run). - Enhanced `handleBall` and `handlePitch` to track `scoredRunners` and update scores dynamically. - Updated history to include both `gameState` and `lineupState`. - Refactored UI components (`GameStateDisplay`) to use unified score structure and display detailed base states. - Styled active lineup tab with visual emphasis. - Introduced utility functions `encodeInning` and `decodeInning` for inning state management.
This commit is contained in:
419
src/App.js
419
src/App.js
@@ -12,25 +12,187 @@ import Tabs from 'react-bootstrap/Tabs';
|
||||
|
||||
const MAX_INNINGS = 1
|
||||
|
||||
// Handle strikes
|
||||
const handleStrike = (prevState) => {
|
||||
let newState = {...prevState}
|
||||
let message
|
||||
const newStrikes = prevState.count.strikes + 1;
|
||||
var endsAtBat = false
|
||||
if (newStrikes === 3) {
|
||||
// Strikeout
|
||||
const outResult = handleOut(newState)
|
||||
endsAtBat = outResult.endsAtBat
|
||||
newState = {...newState, ...outResult.newState};
|
||||
message = ["Strikeout!", outResult.message].join(' ')
|
||||
} else {
|
||||
newState.count.strikes = newStrikes
|
||||
message = `Strike ${newStrikes}.`
|
||||
}
|
||||
return {newState, message, endsAtBat}
|
||||
};
|
||||
|
||||
// Handle fouls
|
||||
const handleFoul = (prevState) => {
|
||||
let newState = {...prevState}
|
||||
let message = "Foul."
|
||||
|
||||
const prevStrikes = prevState.count.strikes;
|
||||
if (prevStrikes === 2) {
|
||||
// Nothing
|
||||
} else {
|
||||
newState.count.strikes = prevStrikes + 1;
|
||||
}
|
||||
return {newState, message}
|
||||
};
|
||||
|
||||
|
||||
// Handle balls
|
||||
const handleBall = (prevState) => {
|
||||
const newBalls = prevState.count.balls + 1;
|
||||
let newState = {...prevState}
|
||||
let message = `Ball ${newBalls}.`
|
||||
var endsAtBat = false
|
||||
if (newBalls === 4) {
|
||||
// Walk (advance batter to 1st base)
|
||||
const advanceResult = advanceRunners(newState, [1,0,0,0]);
|
||||
newState = { ...newState, ...resetCount(advanceResult.newState)};
|
||||
endsAtBat = true
|
||||
console.log(advanceResult)
|
||||
message = [message, "Walk!", advanceResult.message].join(' ')
|
||||
} else {
|
||||
newState.count.balls = newBalls;
|
||||
}
|
||||
return { newState, message, endsAtBat };
|
||||
};
|
||||
|
||||
// Handle outs
|
||||
const handleOut = (prevState) => {
|
||||
let newState = {...prevState}
|
||||
newState = {...newState, ...resetCount(newState)}
|
||||
const newOuts = newState.outs + 1;
|
||||
let message = `${newOuts} out${newOuts > 1 ? "s":""}`
|
||||
if (newOuts === 3) {
|
||||
message += "!"
|
||||
const switchInningResult = switchInning(newState);
|
||||
newState = { ...newState, ...switchInningResult.newState};
|
||||
message = [message, switchInningResult.message].join(" ")
|
||||
} else {
|
||||
message += "."
|
||||
newState = { ...newState, outs: newOuts}
|
||||
}
|
||||
return {newState, message, endsAtBat: true};
|
||||
};
|
||||
|
||||
const resetCount = (prevState) => {
|
||||
return {...prevState, count:{strikes: 0, balls: 0}}
|
||||
}
|
||||
|
||||
// Advance runners and reset 1st base
|
||||
export const advanceRunners = (gameState, advancements) => {
|
||||
// Combine batter and runners into a single array for easier processing
|
||||
var newState = {...gameState}
|
||||
let runnersInclBatter = [newState.currentBatter, ...newState.bases];
|
||||
const newBasesInclBatter = [null, null, null, null]; // First, second, third, and batter
|
||||
const scoredRunners = []; // Runners who score
|
||||
|
||||
var message = ""
|
||||
|
||||
// Recursive function to handle forced advancement
|
||||
const moveRunner = (runner, targetBase) => {
|
||||
if (targetBase >= 4) {
|
||||
// If targetBase >= 4, the runner scores
|
||||
scoredRunners.push(runner);
|
||||
} else if (newBasesInclBatter[targetBase] === null) {
|
||||
// If targetBase is unoccupied, place the runner there
|
||||
newBasesInclBatter[targetBase] = runner;
|
||||
} else {
|
||||
// If targetBase is occupied, recursively move the occupier to the next base
|
||||
const displacedRunner = newBasesInclBatter[targetBase];
|
||||
newBasesInclBatter[targetBase] = runner; // Place current runner here
|
||||
moveRunner(displacedRunner, targetBase + 1); // Move the displaced runner
|
||||
}
|
||||
};
|
||||
|
||||
// Process each runner and their advancement
|
||||
for (let i = 3; i >= 0; i--) {
|
||||
if (runnersInclBatter[i] !== null) {
|
||||
const targetBase = i + advancements[i];
|
||||
moveRunner(runnersInclBatter[i], targetBase);
|
||||
}
|
||||
// console.log(i, runnersInclBatter[i], runnersInclBatter)
|
||||
}
|
||||
newState.bases = newBasesInclBatter.slice(1);
|
||||
if (scoredRunners.length > 0){message=`${scoredRunners.length} runner scored!`}
|
||||
console.log(scoredRunners, scoredRunners.length)
|
||||
return { newState, scoredRunners, message };
|
||||
}
|
||||
|
||||
const isGameFinal = ({inning, isTopHalf, homeScore, awayScore}) => {
|
||||
if (inning >= MAX_INNINGS) {
|
||||
if (isTopHalf && homeScore > awayScore){
|
||||
console.log(homeScore, awayScore, 'no need for bottom of last inning')
|
||||
return true;
|
||||
} else if (!isTopHalf && homeScore != awayScore) {
|
||||
return true;
|
||||
} else {
|
||||
console.log('No decision yet! Keep playing')
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// Switch innings
|
||||
const switchInning = (prevState) => {
|
||||
console.dir(prevState)
|
||||
let message
|
||||
let newState = {...prevState,
|
||||
...resetCount(prevState),
|
||||
bases: [null, null, null],
|
||||
outs: 0,
|
||||
isTopHalf: !prevState.isTopHalf, // Switch halves
|
||||
inning: prevState.isTopHalf ? prevState.inning : prevState.inning + 1 // Increment inning after Bottom
|
||||
}
|
||||
if (isGameFinal(newState)){
|
||||
console.dir(newState)
|
||||
newState = {...newState, isFinal: true}
|
||||
message = "Game over!"
|
||||
} else {
|
||||
message = "Next inning."
|
||||
}
|
||||
return {newState, message}
|
||||
};
|
||||
|
||||
function App() {
|
||||
// Game state
|
||||
const [gameState, setGameState] = useState({
|
||||
bases: [null, null, null], // [1st, 2nd, 3rd]
|
||||
currentBatter: null,
|
||||
inning: 1,
|
||||
isTopHalf: true, // true = Top of inning (away team bats), false = Bottom (home team bats)
|
||||
outs: 0,
|
||||
balls: 0, // Count for current at-bat
|
||||
strikes: 0, // Count for current at-bat
|
||||
currentBatterIndex: { away: 0, home: 0 }, // Tracks the current batter for each team
|
||||
homeScore: 0,
|
||||
awayScore: 0,
|
||||
count: {
|
||||
strikes: 0,
|
||||
balls: 0
|
||||
},
|
||||
score: {
|
||||
home: 0,
|
||||
away: 0
|
||||
},
|
||||
isFinal: false
|
||||
});
|
||||
|
||||
// Separate lineup state for both teams
|
||||
const [lineups, setLineups] = useState({
|
||||
away: [], // Away team's lineup
|
||||
home: [], // Home team's lineup
|
||||
const [lineupState, setLineupState] = useState({
|
||||
away: {
|
||||
players: [],
|
||||
currentBatterIndex: 0
|
||||
},
|
||||
home: {
|
||||
players: [],
|
||||
currentBatterIndex: 0
|
||||
}
|
||||
});
|
||||
|
||||
const [history, setHistory] = useState([]);
|
||||
@@ -39,52 +201,61 @@ function App() {
|
||||
|
||||
// Simulate fetching the lineups on component mount
|
||||
useEffect(() => {
|
||||
const fetchLineups = async () => {
|
||||
const fetchLineups = async () => (
|
||||
// Simulate an API call or database query
|
||||
const fetchedLineups = {
|
||||
{
|
||||
away: ["Player A1", "Player A2", "Player A3", "Player A4", "Player A5"],
|
||||
home: ["Player H1", "Player H2", "Player H3", "Player H4", "Player H5"],
|
||||
};
|
||||
setLineups(fetchedLineups);
|
||||
};
|
||||
}
|
||||
);
|
||||
|
||||
fetchLineups();
|
||||
fetchLineups()
|
||||
.then((fetchedLineups)=>{
|
||||
const newLineupState = {
|
||||
home:{...lineupState.home, players: fetchedLineups.home},
|
||||
away:{...lineupState.away, players: fetchedLineups.away}
|
||||
}
|
||||
setLineupState({...newLineupState})
|
||||
return newLineupState
|
||||
})
|
||||
.then((newLineupState)=>{
|
||||
const activeLineup = getActiveLineup();
|
||||
const currentIndex = getCurrentBatterIndex();
|
||||
const currentBatter = activeLineup[currentIndex];
|
||||
console.log(activeLineup, currentIndex)
|
||||
setGameState({...gameState, currentBatter});
|
||||
});
|
||||
}, []); // Runs only once when the component mounts
|
||||
|
||||
// Get the current batter's index
|
||||
const getCurrentBatterIndex = () =>
|
||||
gameState.isTopHalf
|
||||
? gameState.currentBatterIndex.away
|
||||
: gameState.currentBatterIndex.home;
|
||||
? lineupState.away.currentBatterIndex
|
||||
: lineupState.home.currentBatterIndex;
|
||||
|
||||
// Update the batter index for the active team
|
||||
const advanceLineup = () => {
|
||||
setGameState((prevState) => {
|
||||
const teamKey = prevState.isTopHalf ? "away" : "home";
|
||||
const nextIndex =
|
||||
(prevState.currentBatterIndex[teamKey] + 1) % lineups[teamKey].length;
|
||||
return {
|
||||
...prevState,
|
||||
currentBatterIndex: {
|
||||
...prevState.currentBatterIndex,
|
||||
[teamKey]: nextIndex,
|
||||
},
|
||||
};
|
||||
});
|
||||
const advanceLineup = (prevGameState, prevLineupState, setLineupState) => {
|
||||
console.log('Advancing Lineup')
|
||||
const teamKey = prevGameState.isTopHalf ? "away" : "home";
|
||||
const {currentBatterIndex, players} = prevLineupState[teamKey]
|
||||
var newLineupState = {...prevLineupState}
|
||||
const nextBatterIndex =
|
||||
(currentBatterIndex + 1) % players.length;
|
||||
newLineupState[teamKey].currentBatterIndex = nextBatterIndex
|
||||
setLineupState(newLineupState)
|
||||
};
|
||||
|
||||
const renderLineup = (home_or_away) => {
|
||||
const lineup = lineups[home_or_away];
|
||||
const currentIndex = gameState.currentBatterIndex[home_or_away];
|
||||
const {players, currentBatterIndex} = lineupState[home_or_away];
|
||||
|
||||
return (
|
||||
<Table>
|
||||
<tbody>
|
||||
{lineup.map((player, index) => (
|
||||
{players.map((player, index) => (
|
||||
<tr>
|
||||
<td
|
||||
key={index}
|
||||
className={`lineup-item ${index === currentIndex ? "at-bat" : ""}`}
|
||||
className={`lineup-item ${index === currentBatterIndex ? "at-bat" : ""}`}
|
||||
>
|
||||
{player}
|
||||
</td>
|
||||
@@ -97,7 +268,7 @@ function App() {
|
||||
|
||||
// Function to determine which lineup is active
|
||||
const getActiveLineup = () =>
|
||||
gameState.isTopHalf ? lineups.away : lineups.home;
|
||||
gameState.isTopHalf ? lineupState.away.players : lineupState.home.players;
|
||||
|
||||
// Add an entry to the game log
|
||||
const addToGameLog = (message) => {
|
||||
@@ -106,88 +277,55 @@ function App() {
|
||||
};
|
||||
|
||||
// Function to handle play input
|
||||
const handlePlay = (play) => {
|
||||
const handlePitch = (pitch) => {
|
||||
// Save current state to history before modifying it
|
||||
setHistory((prevHistory) => [...prevHistory, { ...gameState }]);
|
||||
// const currentBatter = getCurrentBatter();
|
||||
const lineup = getActiveLineup();
|
||||
const currentIndex = getCurrentBatterIndex();
|
||||
var gameLogMessage = `${lineup[currentIndex]} batting: ${play}`
|
||||
console.log(`Handle play ${play}`)
|
||||
switch (play) {
|
||||
case "strike":
|
||||
const newState = handleStrike(gameState);
|
||||
gameLogMessage += ` ${newState.isStrikeOut ? "3. Strikeout!": newState.strikes}`
|
||||
break;
|
||||
case "ball":
|
||||
handleBall();
|
||||
break;
|
||||
case "out":
|
||||
handleOut();
|
||||
break;
|
||||
case "foul":
|
||||
handleFoul();
|
||||
break;
|
||||
case "hit":
|
||||
advanceRunners();
|
||||
break;
|
||||
default:
|
||||
console.warn("Unknown play:", play); // Optional: Handle unexpected values
|
||||
}
|
||||
console.log('made it to end')
|
||||
addToGameLog(gameLogMessage)
|
||||
};
|
||||
|
||||
// Handle strikes
|
||||
const handleStrike = (prevState) => {
|
||||
let newState
|
||||
let isStrikeOut = false
|
||||
const newStrikes = prevState.strikes + 1;
|
||||
if (newStrikes === 3) {
|
||||
// Strikeout
|
||||
handleOut();
|
||||
isStrikeOut = true
|
||||
newState = { ...prevState, strikes: 0, balls: 0 }; // Reset count
|
||||
const currentBatter = lineup[currentIndex];
|
||||
const gameLogMessageArray = [`${currentBatter} batting: `]
|
||||
console.log(`Handle play ${pitch}`)
|
||||
let pitchResultState = {...gameState}
|
||||
pitchResultState.currentBatter = currentBatter
|
||||
var endsAtBat
|
||||
var scoredRunners
|
||||
if (pitch === "strike") {
|
||||
const result = handleStrike(pitchResultState)
|
||||
const {newState, message} = result
|
||||
endsAtBat = result.endsAtBat
|
||||
gameLogMessageArray.push(message)
|
||||
pitchResultState = { ...pitchResultState, ...newState };
|
||||
} else if (pitch === "foul") {
|
||||
const result = handleFoul(pitchResultState)
|
||||
const {newState, message} = result
|
||||
endsAtBat = result.endsAtBat
|
||||
gameLogMessageArray.push(message)
|
||||
pitchResultState = { ...pitchResultState, ...newState};
|
||||
} else if (pitch === "ball") {
|
||||
const result = handleBall(pitchResultState)
|
||||
const {newState, message} = result
|
||||
endsAtBat = result.endsAtBat
|
||||
gameLogMessageArray.push(message)
|
||||
pitchResultState = { ...pitchResultState, ...newState };
|
||||
} else if (pitch === "out") {
|
||||
const result = handleOut(pitchResultState)
|
||||
const {newState, message} = result
|
||||
endsAtBat = result.endsAtBat
|
||||
gameLogMessageArray.push(message)
|
||||
pitchResultState = { ...pitchResultState, ...newState };
|
||||
} else if (pitch === "hit") {
|
||||
const result = advanceRunners(pitchResultState, [1,0,0,0]);
|
||||
const {newState, message} = result
|
||||
gameLogMessageArray.push(message)
|
||||
scoredRunners = result.scoredRunners
|
||||
pitchResultState = {...pitchResultState, ...newState}
|
||||
} else {
|
||||
newState = { ...prevState, strikes: newStrikes };
|
||||
console.warn("Unknown play:", pitch); // Optional: Handle unexpected values
|
||||
}
|
||||
|
||||
setGameState(newState)
|
||||
return {...newState, isStrikeOut}
|
||||
};
|
||||
|
||||
// Handle fouls
|
||||
const handleFoul = () => {
|
||||
setGameState((prevState) => {
|
||||
const newStrikes = prevState.strikes == 2 ? prevState.strikes : prevState.strikes + 1;
|
||||
return { ...prevState, strikes: newStrikes };
|
||||
});
|
||||
};
|
||||
|
||||
// Handle balls
|
||||
const handleBall = () => {
|
||||
setGameState((prevState) => {
|
||||
const newBalls = prevState.balls + 1;
|
||||
if (newBalls === 4) {
|
||||
// Walk (advance batter to 1st base)
|
||||
advanceRunners();
|
||||
return { ...prevState, strikes: 0, balls: 0 }; // Reset count
|
||||
}
|
||||
return { ...prevState, balls: newBalls };
|
||||
});
|
||||
};
|
||||
|
||||
// Handle outs
|
||||
const handleOut = () => {
|
||||
advanceLineup(); // Move to the next batter
|
||||
setGameState((prevState) => {
|
||||
const newOuts = prevState.outs + 1;
|
||||
if (newOuts === 3) {
|
||||
// Reset outs and switch inning/half
|
||||
switchInning();
|
||||
return { ...prevState, outs: 0, strikes: 0, balls: 0 }; // Reset count and outs
|
||||
}
|
||||
return { ...prevState, outs: newOuts, strikes: 0, balls: 0 }; // Reset count
|
||||
});
|
||||
setGameState(pitchResultState)
|
||||
if (endsAtBat){advanceLineup(gameState, lineupState, setLineupState)}
|
||||
addToGameLog([...gameLogMessageArray].join(' '))
|
||||
};
|
||||
|
||||
const scoreRunner = (scoringRunner) => {
|
||||
@@ -201,53 +339,6 @@ function App() {
|
||||
})
|
||||
}
|
||||
|
||||
// Advance runners and reset 1st base
|
||||
const advanceRunners = () => {
|
||||
setGameState((prevState) => {
|
||||
const newBases = [...prevState.bases];
|
||||
const activeLineup = getActiveLineup();
|
||||
const currentBatter = activeLineup[getCurrentBatterIndex()];
|
||||
|
||||
for (let i = 2; i >= 0; i--) {
|
||||
if (newBases[i]) {
|
||||
if (i === 2) {
|
||||
scoreRunner(newBases[i])
|
||||
if (prevState.inning >= MAX_INNINGS && prevState.homeScore > prevState.awayScore) {
|
||||
endGame();
|
||||
}
|
||||
} else {
|
||||
newBases[i + 1] = newBases[i];
|
||||
}
|
||||
newBases[i] = null; // Clear current base
|
||||
}
|
||||
}
|
||||
newBases[0] = currentBatter; // Add current batter to 1st base
|
||||
advanceLineup(); // Move to the next batter
|
||||
return { ...prevState, bases: newBases, strikes: 0, balls: 0 }; // Reset count
|
||||
});
|
||||
};
|
||||
|
||||
// Switch innings
|
||||
const switchInning = () => {
|
||||
setGameState((prevState) => {
|
||||
if (prevState.inning >= MAX_INNINGS) {
|
||||
if (prevState.isTopHalf && prevState.homeScore > prevState.awayScore){
|
||||
endGame();
|
||||
return {...prevState};
|
||||
} else if (!prevState.isTopHalf && prevState.homeScore != prevState.awayScore) {
|
||||
endGame();
|
||||
return {...prevState};
|
||||
} else {
|
||||
console.log('No decision yet! Keep playing')
|
||||
}
|
||||
}
|
||||
return {...prevState,
|
||||
isTopHalf: !prevState.isTopHalf, // Switch halves
|
||||
inning: prevState.isTopHalf ? prevState.inning : prevState.inning + 1, // Increment inning after Bottom
|
||||
bases: [null, null, null]}
|
||||
});
|
||||
};
|
||||
|
||||
const endGame = () => {
|
||||
setGameState((prevState)=>({...prevState, isFinal: true}))
|
||||
}
|
||||
@@ -309,14 +400,14 @@ function App() {
|
||||
<Button className="" onClick={()=>handleUndo()} disabled={isUndoDisabled}>
|
||||
<FontAwesomeIcon icon={faUndo} />
|
||||
</Button>
|
||||
<button className="play strike" onClick={() => handlePlay("strike")} disabled={gameState.isFinal}>Strike</button>
|
||||
<button className="play ball" onClick={() => handlePlay("ball")} disabled={gameState.isFinal}>Ball</button>
|
||||
<button className="play foul" onClick={() => handlePlay("foul")} disabled={gameState.isFinal}>Foul</button>
|
||||
<button className="play out" onClick={() => handlePlay("out")} disabled={gameState.isFinal}>Out</button>
|
||||
<button className="play strike" onClick={() => handlePitch("strike")} disabled={gameState.isFinal}>Strike</button>
|
||||
<button className="play ball" onClick={() => handlePitch("ball")} disabled={gameState.isFinal}>Ball</button>
|
||||
<button className="play foul" onClick={() => handlePitch("foul")} disabled={gameState.isFinal}>Foul</button>
|
||||
<button className="play out" onClick={() => handlePitch("out")} disabled={gameState.isFinal}>Out</button>
|
||||
|
||||
<Button className="play hit" onClick={() => handlePlay("hit")}>1</Button>
|
||||
<Button className="play hit" onClick={() => handlePlay("hit")}>2</Button>
|
||||
<Button className="play hit" onClick={() => handlePlay("hit")}>3</Button>
|
||||
<Button className="play hit" onClick={() => handlePitch("hit")}>1</Button>
|
||||
<Button className="play hit" onClick={() => handlePitch("hit")}>2</Button>
|
||||
<Button className="play hit" onClick={() => handlePitch("hit")}>3</Button>
|
||||
|
||||
</section>
|
||||
<section>
|
||||
|
||||
142
src/App.test.js
142
src/App.test.js
@@ -1,8 +1,140 @@
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import App from './App';
|
||||
import { advanceRunners } from './App.js';
|
||||
|
||||
test('renders learn react link', () => {
|
||||
render(<App />);
|
||||
const linkElement = screen.getByText(/learn react/i);
|
||||
expect(linkElement).toBeInTheDocument();
|
||||
});
|
||||
// test('renders learn react link', () => {
|
||||
// render(<App />);
|
||||
// const linkElement = screen.getByText(/learn react/i);
|
||||
// expect(linkElement).toBeInTheDocument();
|
||||
// });
|
||||
|
||||
describe('advanceRunners', () => {
|
||||
it('should advance the batter only on a walk', () => {
|
||||
const batter = "Batter";
|
||||
const prevGameState = {bases: ["Player2", null, null], currentBatter: "Batter"}
|
||||
const advancements = [1, 0, 0, 0];
|
||||
const {newState, scoredRunners} = advanceRunners(prevGameState, advancements);
|
||||
const expected = {
|
||||
newBases: ["Batter", "Player2", null],
|
||||
scoredRunners: [],
|
||||
};
|
||||
expect(newState.bases).toEqual(expected.newBases)
|
||||
expect(scoredRunners).toEqual(scoredRunners);
|
||||
});
|
||||
|
||||
it('should advance runners correctly with forced advancement', () => {
|
||||
const batter = "Batter";
|
||||
const prevGameState = {bases: ["Player2", "Player1", null], currentBatter: "Batter"}
|
||||
const advancements = [1, 0, 0, 0];
|
||||
const {newState, scoredRunners} = advanceRunners(prevGameState, advancements);
|
||||
const expected = {
|
||||
newBases: ["Batter", "Player2", "Player1"],
|
||||
scoredRunners: [],
|
||||
};
|
||||
expect(newState.bases).toEqual(expected.newBases)
|
||||
expect(scoredRunners).toEqual(scoredRunners);
|
||||
});
|
||||
|
||||
it('should handle a double correctly', () => {
|
||||
const batter = "Batter";
|
||||
const prevGameState = {bases: ["Player1", "Player2", "Player3"], currentBatter: "Batter"}
|
||||
const advancements = [2, 2, 2, 1];
|
||||
const {newState, scoredRunners} = advanceRunners(prevGameState, advancements);
|
||||
const expected = {
|
||||
newBases: [null, "Batter" , "Player1"],
|
||||
scoredRunners: ["Player3", "Player2"],
|
||||
};
|
||||
expect(newState.bases).toEqual(expected.newBases)
|
||||
expect(scoredRunners).toEqual(scoredRunners);
|
||||
});
|
||||
|
||||
it('should handle a triple correctly', () => {
|
||||
const batter = "Batter";
|
||||
const prevGameState = {bases: ["Player1", "Player2", "Player3"], currentBatter: "Batter"}
|
||||
const advancements = [3, 3, 2, 1];
|
||||
const {newState, scoredRunners} = advanceRunners(prevGameState, advancements);
|
||||
const expected = {
|
||||
newBases: [null, null, "Batter"],
|
||||
scoredRunners: ["Player3", "Player2", "Player1"],
|
||||
};
|
||||
expect(newState.bases).toEqual(expected.newBases)
|
||||
expect(scoredRunners).toEqual(scoredRunners);
|
||||
});
|
||||
|
||||
it('should handle a home run correctly', () => {
|
||||
const batter = "Batter";
|
||||
const prevGameState = {bases: ["Player1", "Player2", "Player3"], currentBatter: "Batter"}
|
||||
const advancements = [4, 3, 2, 1];
|
||||
const {newState, scoredRunners} = advanceRunners(prevGameState, advancements);
|
||||
const expected = {
|
||||
newBases: [null, null, null],
|
||||
scoredRunners: ["Player3", "Player2", "Player1", "Batter"],
|
||||
};
|
||||
expect(newState.bases).toEqual(expected.newBases)
|
||||
expect(scoredRunners).toEqual(scoredRunners);
|
||||
});
|
||||
|
||||
it('should handle no runners correctly', () => {
|
||||
const batter = "Batter";
|
||||
const prevGameState = {bases: [null, null, null, null], currentBatter: "Batter"}
|
||||
const advancements = [0, 0, 0, 0];
|
||||
const {newState, scoredRunners} = advanceRunners(prevGameState, advancements);
|
||||
const expected = {
|
||||
newBases: [null, null, null],
|
||||
scoredRunners: [],
|
||||
};
|
||||
expect(newState.bases).toEqual(expected.newBases)
|
||||
expect(scoredRunners).toEqual(scoredRunners);
|
||||
});
|
||||
|
||||
it('should handle fully loaded bases with a walk correctly', () => {
|
||||
const batter = "Batter";
|
||||
const prevGameState = {bases: ["Player1", "Player2", "Player3"], currentBatter: "Batter"}
|
||||
const advancements = [1, 0, 0, 0];
|
||||
const {newState, scoredRunners} = advanceRunners(prevGameState, advancements);
|
||||
const expected = {
|
||||
newBases: ["Batter", "Player1", "Player2"],
|
||||
scoredRunners: ["Player3"],
|
||||
};
|
||||
expect(newState.bases).toEqual(expected.newBases)
|
||||
expect(scoredRunners).toEqual(scoredRunners);
|
||||
});
|
||||
|
||||
it('should handle single with runner on second base correctly', () => {
|
||||
const batter = "Batter";
|
||||
const prevGameState = {bases: [null, "Player1", null], currentBatter: "Batter"}
|
||||
const advancements = [1, 1, 1, 1];
|
||||
const {newState, scoredRunners} = advanceRunners(prevGameState, advancements);
|
||||
const expected = {
|
||||
newBases: ["Batter", null, "Player1"],
|
||||
scoredRunners: [],
|
||||
};
|
||||
expect(newState.bases).toEqual(expected.newBases)
|
||||
expect(scoredRunners).toEqual(scoredRunners);
|
||||
});
|
||||
|
||||
it('should handle double with runner on second base correctly', () => {
|
||||
const batter = "Batter";
|
||||
const prevGameState = {bases: [null, "Player1", null], currentBatter: "Batter"}
|
||||
const advancements = [2, 2, 2, 1];
|
||||
const {newState, scoredRunners} = advanceRunners(prevGameState, advancements);
|
||||
const expected = {
|
||||
newBases: [null, "Batter", null],
|
||||
scoredRunners: ["Player1"],
|
||||
};
|
||||
expect(newState.bases).toEqual(expected.newBases)
|
||||
expect(scoredRunners).toEqual(scoredRunners);
|
||||
});
|
||||
it('should handle walk with runner on third base correctly', () => {
|
||||
const batter = "Batter";
|
||||
const prevGameState = {bases: [null, null, "Player1"], currentBatter: "Batter"}
|
||||
const advancements = [1, 0, 0, 0];
|
||||
const {newState, scoredRunners} = advanceRunners(prevGameState, advancements);
|
||||
const expected = {
|
||||
newBases: ["Batter", null, "Player1"],
|
||||
scoredRunners: [],
|
||||
};
|
||||
expect(newState.bases).toEqual(expected.newBases)
|
||||
expect(scoredRunners).toEqual(scoredRunners);
|
||||
});
|
||||
});
|
||||
@@ -13,7 +13,7 @@ function Inning({inning, isTopHalf}){
|
||||
)
|
||||
}
|
||||
|
||||
function GameStateDisplay({inning, bases, isTopHalf, outs, balls, strikes, awayScore, homeScore, isFinal }){
|
||||
function GameStateDisplay({inning, bases, isTopHalf, outs, count, awayScore, homeScore, isFinal }){
|
||||
return (
|
||||
<div className="gameState">
|
||||
<header>Scoreboard</header>
|
||||
@@ -21,7 +21,7 @@ function GameStateDisplay({inning, bases, isTopHalf, outs, balls, strikes, away
|
||||
<div>
|
||||
<Row>
|
||||
<Col><Inning inning={inning} isTopHalf={isTopHalf}></Inning></Col>
|
||||
<Col>{balls}-{strikes}</Col>
|
||||
<Col>{count.balls}-{count.strikes}</Col>
|
||||
<Col>{outs} outs</Col>
|
||||
<Col>
|
||||
<FontAwesomeIcon icon={fa1} className={`base ${bases[0] ? "occupied" : ""}`}></FontAwesomeIcon>
|
||||
|
||||
Reference in New Issue
Block a user