01Why does this project exist?
I have a lovely family of four. It was a nice day - the first day of the Lunar New Year in Vietnam, which we call Tết. We decided to play a board game called Tiến Lên, a popular card game in Vietnam. We had a lot of fun playing together.
We kept score on paper, which is very traditional, but by the end of the game we realized we had lost track of some points and it was difficult to figure out where the mistakes were. At that moment, I realized that keeping score can be quite cumbersome and distracting from the enjoyment of the game.
That's when I thought about creating an app to help us and other players easily manage and track scores. Fortunately, I had free time during the Tết holiday and decided to learn Next.js by building this idea. This project was born out of a desire to enhance our gaming experience and make it more enjoyable for everyone involved.
02Key Features
03Screenshots
Designed for phones. Tap any screenshot to enlarge.




04Tech Stack
Each technology was chosen deliberately. This was my first real Next.js project - the goal was to learn the framework while shipping something genuinely useful.
05Under the Hood
The core of the app is a score reducer that handles all game state - adding players, submitting round scores, and computing the running totals with penalty logic.
TypeScript
// Score reducer - handles round submissions
function scoreReducer(state, action) {
switch (action.type) {
case 'SUBMIT_ROUND': {
const { scores, penalty } = action.payload
return {
...state,
rounds: [...state.rounds, scores],
totals: state.players.map((p, i) => ({
...p,
score: p.score + scores[i] - (penalty[i] ?? 0)
}))
}
}
case 'ADD_PLAYER':
return { ...state, players: [...state.players, action.payload] }
default:
return state
}
}