Refactor game state management and enhance scoring logic

- Added `handleHit` function to handle various hit types (single, double, triple, home run).
- Refactored `handleBall`, `handleOut`, and `handlePitch` to return `scoredRunners` and update scores accordingly.
- Updated history management to include both `gameState` and `lineupState`.
- Improved scoreboard rendering to reflect updated scoring structure and base occupation styles.
- Enhanced navigation button styling for active lineup tab.
- Fixed UI to dynamically display scored runners and adjusted base styling in `GameStateDisplay`.
This commit is contained in:
2024-12-12 06:25:00 -06:00
parent 7203ac4c2d
commit d2146e7c5a
4 changed files with 88 additions and 33 deletions

View File

@@ -52,17 +52,18 @@ const handleBall = (prevState) => {
let newState = {...prevState}
let message = `Ball ${newBalls}.`
var endsAtBat = false
var scoredRunners = []
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)
scoredRunners = advanceResult.scoredRunners
message = [message, "Walk!", advanceResult.message].join(' ')
} else {
newState.count.balls = newBalls;
}
return { newState, message, endsAtBat };
return { newState, message, scoredRunners, endsAtBat };
};
// Handle outs
@@ -78,11 +79,43 @@ const handleOut = (prevState) => {
message = [message, switchInningResult.message].join(" ")
} else {
message += "."
newState = { ...newState, outs: newOuts}
newState.outs = newOuts
}
return {newState, message, endsAtBat: true};
};
const handleHit = (prevState, hit_type) => {
var newState = {...prevState}
var message = `${hit_type}!`
var basesToAdvance
var endsAtBat = false
var scoredRunners = []
switch (hit_type) {
case "single":
basesToAdvance = [1,1,1,1];
break;
case "double":
basesToAdvance = [2,2,2,1];
break;
case "triple":
basesToAdvance = [3,2,2,1];
break;
case "home run":
basesToAdvance = [4,3,2,1];
break;
default:
console.warn("Unknown pitch type:", hit_type);
return; // Exit if pitch is not recognized
}
const advancedResult = advanceRunners(newState, basesToAdvance);
newState = { ...newState, ...resetCount(advancedResult.newState)};
endsAtBat = true
scoredRunners = advancedResult.scoredRunners
message = [message, advancedResult.message].join(' ')
return { newState, message, scoredRunners, endsAtBat };
}
const resetCount = (prevState) => {
return {...prevState, count:{strikes: 0, balls: 0}}
}
@@ -279,7 +312,9 @@ function App() {
// Function to handle play input
const handlePitch = (pitch) => {
// Save current state to history before modifying it
setHistory((prevHistory) => [...prevHistory, { ...gameState }]);
console.dir(gameState)
setHistory((prevHistory) => [...prevHistory, { gameState:{...gameState}, lineupState:{...lineupState}}]);
console.dir(history)
// const currentBatter = getCurrentBatter();
const lineup = getActiveLineup();
const currentIndex = getCurrentBatterIndex();
@@ -289,7 +324,7 @@ function App() {
let pitchResultState = {...gameState}
pitchResultState.currentBatter = currentBatter
var endsAtBat
var scoredRunners
var scoredRunners = []
if (pitch === "strike") {
const result = handleStrike(pitchResultState)
const {newState, message} = result
@@ -306,6 +341,7 @@ function App() {
const result = handleBall(pitchResultState)
const {newState, message} = result
endsAtBat = result.endsAtBat
scoredRunners= result.scoredRunners
gameLogMessageArray.push(message)
pitchResultState = { ...pitchResultState, ...newState };
} else if (pitch === "out") {
@@ -314,31 +350,32 @@ function App() {
endsAtBat = result.endsAtBat
gameLogMessageArray.push(message)
pitchResultState = { ...pitchResultState, ...newState };
} else if (pitch === "hit") {
const result = advanceRunners(pitchResultState, [1,0,0,0]);
} else if (
pitch === "single" ||
pitch === "double" ||
pitch === "triple" ||
pitch === "home run"
) {
const result = handleHit(pitchResultState, pitch)
const {newState, message} = result
gameLogMessageArray.push(message)
scoredRunners = result.scoredRunners
pitchResultState = {...pitchResultState, ...newState}
endsAtBat = result.endsAtBat
scoredRunners= result.scoredRunners
gameLogMessageArray.push(message)
pitchResultState = { ...pitchResultState, ...newState };
} else {
console.warn("Unknown play:", pitch); // Optional: Handle unexpected values
}
if (gameState.isTopHalf){
pitchResultState.score.away += scoredRunners.length
} else {
pitchResultState.score.home += scoredRunners.length
}
addToGameLog([...gameLogMessageArray].join(' '))
setGameState(pitchResultState)
if (endsAtBat){advanceLineup(gameState, lineupState, setLineupState)}
addToGameLog([...gameLogMessageArray].join(' '))
};
const scoreRunner = (scoringRunner) => {
setGameState((prevState)=>{
const homeScore = gameState.isTopHalf ? prevState.homeScore : prevState.homeScore + 1
const awayScore = gameState.isTopHalf ? prevState.awayScore + 1 : prevState.awayScore
const activeLineup = getActiveLineup();
const currentBatter = activeLineup[getCurrentBatterIndex()];
return {...prevState, homeScore, awayScore}
})
}
const endGame = () => {
setGameState((prevState)=>({...prevState, isFinal: true}))
}
@@ -351,10 +388,13 @@ function App() {
// Undo the last action
const handleUndo = () => {
console.dir(history)
if (history.length > 0) {
const lastState = history[history.length - 1];
const {gameState, lineupState} = history[history.length - 1];
const latestGameLog = gameLog[gameLog.length-1]
setGameState(lastState); // Revert to the last state
setGameState(gameState); // Revert to the last state
setLineupState(lineupState); // Revert to the last state
setHistory((prevHistory) => prevHistory.slice(0, -1)); // Remove the last history entry
setGameLog((prevGamelong) => prevGamelong.slice(0, -1)); // Remove the last history entry
}
@@ -405,9 +445,9 @@ function App() {
<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={() => handlePitch("hit")}>1</Button>
<Button className="play hit" onClick={() => handlePitch("hit")}>2</Button>
<Button className="play hit" onClick={() => handlePitch("hit")}>3</Button>
<Button className="play hit" onClick={() => handlePitch("single")}>1</Button>
<Button className="play hit" onClick={() => handlePitch("double")}>2</Button>
<Button className="play hit" onClick={() => handlePitch("triple")}>3</Button>
</section>
<section>

View File

@@ -96,4 +96,12 @@ body {
}
border: none;
}
.lineups.nav {
.nav-item button[role=tab][aria-selected=true]{
font-weight: 900;
&:before, &:after{
content: "*";
}
}
}
}

View File

@@ -13,7 +13,7 @@ function Inning({inning, isTopHalf}){
)
}
function GameStateDisplay({inning, bases, isTopHalf, outs, count, awayScore, homeScore, isFinal }){
function GameStateDisplay({inning, bases, isTopHalf, outs, count, score, isFinal }){
return (
<div className="gameState">
<header>Scoreboard</header>
@@ -24,9 +24,9 @@ function GameStateDisplay({inning, bases, isTopHalf, outs, count, awayScore, ho
<Col>{count.balls}-{count.strikes}</Col>
<Col>{outs} outs</Col>
<Col>
<FontAwesomeIcon icon={fa1} className={`base ${bases[0] ? "occupied" : ""}`}></FontAwesomeIcon>
<FontAwesomeIcon icon={fa2} className={`base ${bases[1] ? "occupied" : ""}`}></FontAwesomeIcon>
<FontAwesomeIcon icon={fa3} className={`base ${bases[2] ? "occupied" : ""}`}></FontAwesomeIcon>
<FontAwesomeIcon icon={fa1} className={`base ${bases[0] ? `occupied ${bases[0]}` : ""}`}></FontAwesomeIcon>
<FontAwesomeIcon icon={fa2} className={`base ${bases[1] ? `occupied ${bases[1]}` : ""}`}></FontAwesomeIcon>
<FontAwesomeIcon icon={fa3} className={`base ${bases[2] ? `occupied ${bases[2]}` : ""}`}></FontAwesomeIcon>
</Col>
</Row>
<Row>
@@ -35,8 +35,8 @@ function GameStateDisplay({inning, bases, isTopHalf, outs, count, awayScore, ho
)}
<strong>{isFinal ? "FINAL!" : ""}</strong>
<Row>
<Col><strong> Away Score: </strong> {awayScore}</Col>
<Col><strong> Home Score: </strong> {homeScore}</Col>
<Col><strong> Away Score: </strong> {score.away}</Col>
<Col><strong> Home Score: </strong> {score.home}</Col>
</Row>
</div>
);

7
src/utils/inningUtils.js Normal file
View File

@@ -0,0 +1,7 @@
const encodeInning = (inningNumber, isTopHalf) =>
inningNumber * 10 + (isTopHalf ? 1 : 2);
const decodeInning = (inningInt) => ({
inningNumber: Math.floor(inningInt / 10),
isTopHalf: inningInt % 10 === 1,
});