123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126 |
- type PlayerOne = {
- kind: "playerOne";
- name: string;
- };
- type PlayerTwo = {
- kind: "playerTwo";
- name: string;
- };
- type Player = PlayerOne | PlayerTwo;
- type Point = "love" | "15" | "30";
- type Deuce = {
- kind: "deuce";
- };
- type PointsData = {
- kind: "points";
- playerOnePoint: Point;
- playerTwoPoint: Point;
- };
- type FortyData = {
- kind: "forty";
- player: Player;
- otherPlayerPoint: Point;
- };
- type Advantage = {
- kind: "advantage",
- player: Player
- };
- type Game = {
- kind: "game",
- player: Player
- // otherPlayerPoint: ? Not representable here.
- };
- type Score = Deuce | PointsData | FortyData | Advantage | Game;
- // Add the ignored _score parameter to ensure a type check.
- function scoreWhenDeuce(_score: Deuce, player: Player): Advantage {
- return {
- kind: "advantage",
- player
- };
- }
- function scoreWhenAdvantage(score: Advantage, player: Player): Game | Deuce {
- return score.player.kind === player.kind ?
- {
- kind: "game",
- player: score.player // == player.
- } :
- {
- kind: "deuce"
- };
- }
- function incrementPoint(point: Point): "15" | "30" | undefined {
- switch (point) {
- case "love":
- return "15";
- case "15":
- return "30";
- case "30":
- return undefined;
- }
- }
- function scoreWhenForty(score: FortyData, player: Player): Game | Deuce | FortyData {
- if (score.player.kind === player.kind) {
- return {
- kind: "game",
- player: player
- };
- } else {
- // "15" | "30" | undefined is a subset of Point | undefined
- const newPoints: Point | undefined = incrementPoint(score.otherPlayerPoint);
- return (newPoints === undefined) ?
- { kind: "deuce" } :
- {...score, otherPlayerPoint: newPoints};
- }
- }
- function updatePointsData(score: PointsData, newPoints: Point, player: Player): PointsData {
- // Discriminated union, with player.kind being the discriminant, player the
- // union, and the switch the typeGuard.
- switch (player.kind) {
- case "playerOne":
- return {...score, playerOnePoint: newPoints};
- case "playerTwo":
- return {...score, playerTwoPoint: newPoints};
- }
- }
- function createFortyData(score: PointsData, player: Player): FortyData {
- let otherPlayerPoint = player.kind === "playerOne"
- ? score.playerTwoPoint
- : score.playerOnePoint;
- return {
- kind: "forty",
- player,
- otherPlayerPoint
- };
- }
- function scoreWhenPoints(score: PointsData, player: Player): PointsData | FortyData {
- const newPoints: Point | undefined = player.kind === "playerOne" ?
- incrementPoint(score.playerOnePoint) :
- incrementPoint(score.playerTwoPoint);
- return newPoints === undefined ?
- createFortyData(score, player) :
- updatePointsData(score, newPoints, player);
- }
- function score(score: Score, player: Player): Score {
- // XXX: refactor to avoid the switch and just use a method map.
- switch (score.kind) {
- case "points":
- return scoreWhenPoints(score, player);
- case "forty":
- return scoreWhenForty(score, player);
- case "deuce":
- return scoreWhenDeuce(score, player);
- case "advantage":
- return scoreWhenAdvantage(score, player);
- case "game":
- return score;
- }
- }
|