From f00f8dadce52902196ef25d4deac538ed206b972 Mon Sep 17 00:00:00 2001 From: Henry Allsuch Date: Wed, 9 Jul 2025 20:34:54 +0100 Subject: [PATCH 1/4] adds simple swift game (with help from claude) --- .../swift/Tennis.playground/Contents.swift | 291 ++++++++++++++++++ .../Tennis.playground/contents.xcplayground | 2 + .../contents.xcworkspacedata | 7 + .../UserInterfaceState.xcuserstate | Bin 0 -> 19155 bytes 4 files changed, 300 insertions(+) create mode 100644 examples/swift/Tennis.playground/Contents.swift create mode 100644 examples/swift/Tennis.playground/contents.xcplayground create mode 100644 examples/swift/Tennis.playground/playground.xcworkspace/contents.xcworkspacedata create mode 100644 examples/swift/Tennis.playground/playground.xcworkspace/xcuserdata/henryallsuch.xcuserdatad/UserInterfaceState.xcuserstate diff --git a/examples/swift/Tennis.playground/Contents.swift b/examples/swift/Tennis.playground/Contents.swift new file mode 100644 index 0000000..d2082cb --- /dev/null +++ b/examples/swift/Tennis.playground/Contents.swift @@ -0,0 +1,291 @@ +import UIKit + +enum GameState { + case warmup + case playing + case deuce + case won + case lost +} + +enum Points { + case love + case fifteen + case thirty + case forty + case avantage + case won +} + +struct Player { + let points : Points + + init(points: Points = .love) { + self.points = points + } +} + +struct Game { + + let state : GameState + + let players : Players + + init(state: GameState = .warmup, players: Players = Players()) { + self.state = state + self.players = players + } + +} + +enum PlayerType { + case server + case reciever +} + +struct Players { + + let server: Player + let reciever: Player + + init(server: Player = Player(), reciever: Player = Player()) { + self.server = server + self.reciever = reciever + } + +} + +struct Umpire { + + static func awardPoints(to player: PlayerType, game: Game) -> Game { + + // First, increment the player's points + let updatedPlayers = incrementPlayerPoints(player: player, players: game.players) + + // Check if game is won after awarding points + let isGameWon = byAwardingPlayerPointIsGameWon(to: player, game: Game(state: game.state, players: updatedPlayers)) + + if(isGameWon){ + + print("Game won by (\(player))") + return Game(state: .won, players: updatedPlayers) + } + + // Check if game should be in deuce state + let newState = determineGameState(players: updatedPlayers, currentState: game.state) + + return Game(state: newState, players: updatedPlayers) + + } + + static func incrementPlayerPoints(player: PlayerType, players: Players) -> Players { + if player == .server { + let newServerPoints = nextPoint(from: players.server.points) + let updatedServer = Player(points: newServerPoints) + return Players(server: updatedServer, reciever: players.reciever) + } else { + let newReceiverPoints = nextPoint(from: players.reciever.points) + let updatedReceiver = Player(points: newReceiverPoints) + return Players(server: players.server, reciever: updatedReceiver) + } + } + + static func nextPoint(from currentPoints: Points) -> Points { + switch currentPoints { + case .love: + return .fifteen + case .fifteen: + return .thirty + case .thirty: + return .forty + case .forty: + return .avantage + case .avantage: + return .won + case .won: + return .won + } + } + + static func determineGameState(players: Players, currentState: GameState) -> GameState { + let serverPoints = players.server.points + let receiverPoints = players.reciever.points + + // Check for deuce condition (both at forty) + if serverPoints == .forty && receiverPoints == .forty { + return .deuce + } + + // If either player has advantage, the game is in playing state + if serverPoints == .avantage || receiverPoints == .avantage { + return .playing + } + + return .playing + } + + static func byAwardingPlayerPointIsGameWon(to player: PlayerType, game: Game) -> Bool { + + let serverPoints = game.players.server.points + let receiverPoints = game.players.reciever.points + + if player == .server { + // Server wins if: + // 1. They have advantage and receiver doesn't have forty (advantage -> win) + // 2. They have forty and receiver has less than forty (straight win) + // 3. They would get .won points (meaning they had advantage and got another point) + return (serverPoints == .avantage && receiverPoints != .forty) || + (serverPoints == .forty && receiverPoints != .forty && receiverPoints != .avantage) || + (serverPoints == .won) + } else { + // Receiver wins if: + // 1. They have advantage and server doesn't have forty (advantage -> win) + // 2. They have forty and server has less than forty (straight win) + // 3. They would get .won points (meaning they had advantage and got another point) + return (receiverPoints == .avantage && serverPoints != .forty) || + (receiverPoints == .forty && serverPoints != .forty && serverPoints != .avantage) || + (receiverPoints == .won) + } + + } + +} + + +struct Scoreboard { + + static func printGame(game: Game) { + + print("Game state: \(game.state)") + print("Player 1: \(game.players.server.points). Player 2: \(game.players.reciever.points).") + + } +} + + +// MARK: - Playground Tests +func testInitialGameState() { + print("\n๐Ÿงช Testing Initial Game State...") + let game = Game() + + assertEqual(game.state, .warmup, "Game should start in warmup state") + assertEqual(game.players.server.points, .love, "Server should start with love") + assertEqual(game.players.reciever.points, .love, "Receiver should start with love") +} + +func testPointProgression() { + print("\n๐Ÿงช Testing Point Progression...") + var game = Game() + + // Award first point to server + game = Umpire.awardPoints(to: .server, game: game) + assertEqual(game.players.server.points, .fifteen, "Server should have fifteen after first point") + assertEqual(game.players.reciever.points, .love, "Receiver should still have love") + assertEqual(game.state, .playing, "Game should be in playing state") + + // Award second point to server + game = Umpire.awardPoints(to: .server, game: game) + assertEqual(game.players.server.points, .thirty, "Server should have thirty after second point") + + // Award third point to server + game = Umpire.awardPoints(to: .server, game: game) + assertEqual(game.players.server.points, .forty, "Server should have forty after third point") +} + +func testServerWinsGame() { + print("\n๐Ÿงช Testing Server Wins Game...") + var game = Game() + + // Server wins 4-0 + game = Umpire.awardPoints(to: .server, game: game) // 15-0 + game = Umpire.awardPoints(to: .server, game: game) // 30-0 + game = Umpire.awardPoints(to: .server, game: game) // 40-0 + game = Umpire.awardPoints(to: .server, game: game) // Game won + + assertEqual(game.state, .won, "Game should be won") +} + +func testDeuceScenario() { + print("\n๐Ÿงช Testing Deuce Scenario...") + var game = Game() + + // Both players reach 40 (deuce) + game = Umpire.awardPoints(to: .server, game: game) // 15-0 + game = Umpire.awardPoints(to: .server, game: game) // 30-0 + game = Umpire.awardPoints(to: .server, game: game) // 40-0 + + game = Umpire.awardPoints(to: .reciever, game: game) // 40-15 + game = Umpire.awardPoints(to: .reciever, game: game) // 40-30 + game = Umpire.awardPoints(to: .reciever, game: game) // 40-40 (deuce) + + assertEqual(game.state, .deuce, "Game should be in deuce state") + assertEqual(game.players.server.points, .forty, "Server should have forty") + assertEqual(game.players.reciever.points, .forty, "Receiver should have forty") +} + +func testAdvantageAndWin() { + print("\n๐Ÿงช Testing Advantage and Win...") + var game = Game() + + // Set up deuce scenario + game = Game(state: .deuce, players: Players( + server: Player(points: .forty), + reciever: Player(points: .forty) + )) + + // Server gets advantage + game = Umpire.awardPoints(to: .server, game: game) + assertEqual(game.players.server.points, .avantage, "Server should have advantage") + assertEqual(game.players.reciever.points, .forty, "Receiver should still have forty") + assertEqual(game.state, .playing, "Game should be in playing state") + + // Server wins the game + game = Umpire.awardPoints(to: .server, game: game) + assertEqual(game.state, .won, "Game should be won") +} + +func testReceiverWinsGame() { + print("\n๐Ÿงช Testing Receiver Wins Game...") + var game = Game() + + // Receiver wins 4-1 + game = Umpire.awardPoints(to: .server, game: game) // 15-0 + game = Umpire.awardPoints(to: .reciever, game: game) // 15-15 + game = Umpire.awardPoints(to: .reciever, game: game) // 15-30 + game = Umpire.awardPoints(to: .reciever, game: game) // 15-40 + game = Umpire.awardPoints(to: .reciever, game: game) // Game won by receiver + + assertEqual(game.state, .won, "Game should be won by receiver") +} + +// Run the tests +print("๐Ÿธ Running Tennis Game Tests...") + +testInitialGameState() +testPointProgression() +testServerWinsGame() +testDeuceScenario() +testAdvantageAndWin() +testReceiverWinsGame() + +print("\n๐ŸŽพ All tests completed!") + + +// Simple test assertion functions for playground +func assertEqual(_ actual: T, _ expected: T, _ message: String = "") { + if actual == expected { + print("โœ… PASS: \(message)") + } else { + print("โŒ FAIL: \(message)") + print(" Expected: \(expected)") + print(" Actual: \(actual)") + } +} + +func assertTrue(_ condition: Bool, _ message: String = "") { + if condition { + print("โœ… PASS: \(message)") + } else { + print("โŒ FAIL: \(message)") + } +} diff --git a/examples/swift/Tennis.playground/contents.xcplayground b/examples/swift/Tennis.playground/contents.xcplayground new file mode 100644 index 0000000..7f43ce8 --- /dev/null +++ b/examples/swift/Tennis.playground/contents.xcplayground @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/examples/swift/Tennis.playground/playground.xcworkspace/contents.xcworkspacedata b/examples/swift/Tennis.playground/playground.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..ca3329e --- /dev/null +++ b/examples/swift/Tennis.playground/playground.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/examples/swift/Tennis.playground/playground.xcworkspace/xcuserdata/henryallsuch.xcuserdatad/UserInterfaceState.xcuserstate b/examples/swift/Tennis.playground/playground.xcworkspace/xcuserdata/henryallsuch.xcuserdatad/UserInterfaceState.xcuserstate new file mode 100644 index 0000000000000000000000000000000000000000..cbcd0185e0358cc3baab55437ec1740c2982c45d GIT binary patch literal 19155 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 Pi2U^68}3=Pm?@K literal 0 HcmV?d00001 From c95e77f1f3599eaeb308ee7d588fbef0455ea533 Mon Sep 17 00:00:00 2001 From: Henry Allsuch Date: Wed, 9 Jul 2025 20:39:00 +0100 Subject: [PATCH 2/4] adds reset to deuce --- .../swift/Tennis.playground/Contents.swift | 67 ++++++++++++++++++ .../UserInterfaceState.xcuserstate | Bin 19155 -> 19040 bytes 2 files changed, 67 insertions(+) diff --git a/examples/swift/Tennis.playground/Contents.swift b/examples/swift/Tennis.playground/Contents.swift index d2082cb..fe1ba4b 100644 --- a/examples/swift/Tennis.playground/Contents.swift +++ b/examples/swift/Tennis.playground/Contents.swift @@ -59,6 +59,23 @@ struct Umpire { static func awardPoints(to player: PlayerType, game: Game) -> Game { + // Handle special case: if opponent has advantage, reset to deuce + if game.state == .playing { + if player == .server && game.players.reciever.points == .avantage { + let resetPlayers = Players( + server: Player(points: .forty), + reciever: Player(points: .forty) + ) + return Game(state: .deuce, players: resetPlayers) + } else if player == .reciever && game.players.server.points == .avantage { + let resetPlayers = Players( + server: Player(points: .forty), + reciever: Player(points: .forty) + ) + return Game(state: .deuce, players: resetPlayers) + } + } + // First, increment the player's points let updatedPlayers = incrementPlayerPoints(player: player, players: game.players) @@ -244,6 +261,54 @@ func testAdvantageAndWin() { assertEqual(game.state, .won, "Game should be won") } +func testCompleteDeuceBattle() { + print("\n๐Ÿงช Testing Complete Deuce Battle...") + var game = Game() + + // Set up deuce + game = Game(state: .deuce, players: Players( + server: Player(points: .forty), + reciever: Player(points: .forty) + )) + + // Server gets advantage + game = Umpire.awardPoints(to: .server, game: game) + assertEqual(game.state, .playing, "Game should be playing after advantage") + assertEqual(game.players.server.points, .avantage, "Server should have advantage") + + // Receiver equalizes - back to deuce + game = Umpire.awardPoints(to: .reciever, game: game) + assertEqual(game.state, .deuce, "Game should be back to deuce") + assertEqual(game.players.server.points, .forty, "Server should be back to forty") + assertEqual(game.players.reciever.points, .forty, "Receiver should be back to forty") + + // Receiver gets advantage this time + game = Umpire.awardPoints(to: .reciever, game: game) + assertEqual(game.state, .playing, "Game should be playing with receiver advantage") + assertEqual(game.players.reciever.points, .avantage, "Receiver should have advantage") + + // Receiver wins the game + game = Umpire.awardPoints(to: .reciever, game: game) + assertEqual(game.state, .won, "Game should be won by receiver") +} + +func testAdvantageBackToDeuce() { + print("\n๐Ÿงช Testing Advantage Back to Deuce...") + var game = Game() + + // Set up advantage scenario - server has advantage + game = Game(state: .playing, players: Players( + server: Player(points: .avantage), + reciever: Player(points: .forty) + )) + + // Receiver wins point, should go back to deuce + game = Umpire.awardPoints(to: .reciever, game: game) + assertEqual(game.state, .deuce, "Game should be back to deuce") + assertEqual(game.players.server.points, .forty, "Server should be back to forty") + assertEqual(game.players.reciever.points, .forty, "Receiver should be back to forty") +} + func testReceiverWinsGame() { print("\n๐Ÿงช Testing Receiver Wins Game...") var game = Game() @@ -266,6 +331,8 @@ testPointProgression() testServerWinsGame() testDeuceScenario() testAdvantageAndWin() +testCompleteDeuceBattle() +testAdvantageBackToDeuce() testReceiverWinsGame() print("\n๐ŸŽพ All tests completed!") diff --git a/examples/swift/Tennis.playground/playground.xcworkspace/xcuserdata/henryallsuch.xcuserdatad/UserInterfaceState.xcuserstate b/examples/swift/Tennis.playground/playground.xcworkspace/xcuserdata/henryallsuch.xcuserdatad/UserInterfaceState.xcuserstate index cbcd0185e0358cc3baab55437ec1740c2982c45d..0041a05e5399033bfb139ac7407cd0136a2ab878 100644 GIT binary patch delta 6542 zcmai&2V4}_*1+c$K#kdDJKGR<*+rJ!h3(50cC8VkJPV>?ML|WefLO5Y9b=81G4=*n zF!o?CvBVmU1w^pMSctJFrm3iJfZ+Gu%lE!-f4`ZzJ3Hr|bIphOoth8U}rc84u+X<2pkHt;A9wp1k!K{{0vTo)8Kr#04{|2a5Y>5*TMo=2sglu za4Xyfzk(%jAKVX*z@zXOJPW^t=imi+5k7`b7(J8*-of_>BM#!CP{c!gBtS|Oj+&qd z6p5OlXkV&$Y9w-&{MFY?vG#F)}iD(j5j;;pKb@T(eiyooJ=n482{f6FS z3IhyrFm8lD!U8PBVOW79a5Rp?R_w&_*o(X1WZV^};BL4(?txQrPuvUl!-Mb$JQ8Q) z33wu&geT)Ecsibk=i?=KDPD$G;nny{ycuu7Tk$r003XDM@L{a}8kgcSd1TmjtXfz<8xH^E5D%j3asMF`mdmeW9&GJCso$Qyhv5=7EKjxDd<-3&=z=sSxA? zhD;{YnAy^HO_qX{l(+~i1Ixh*5+H=oMPLEt#rqxel2AKx5O3f8l; zZD6YHkqy=UQJ1Ki53@=Z)1+S0Hn5Y`M3sXb#80Ldg0DacnL%bUFJzLyUMg`dk?~^Z zsr|Kr6cw6SFLxLmt&zJAj*vEF&IdW-?UO!Y!02Ha!*knb3?Gy`_%f^i6q#2_b*8$2 zGvI7WQkSIBSy_eP43$z^yVi45Vgbb`h{)>az6dT;_5!LOd$RzPl7%F(?#XgcQTJqi z-E`N$^*%{mI*iUu>z|d;JSQhDuLxYD`co-+y>v1MdS;zxCs0J?t*({ z30X>(F;((81AhXKDEr#AWQ8^;gbwIr z4ckVxlZ|8t*|`C_pc{Iimp!}5SEPjOB74|#3G+;~HPDt-n+V%=OX_0G%^a4YZho3O zxpVpIp=5WR3GHErI&B$+usxO1CwWAAT3$q2ZiG3`WpUP9&=K~a?8UGX>^TMYt+^6YBXX3qWpnK?IaU%G*&6tU!O@g` zBODG#z>zQ;=D<-fmmDYGkQ3x2IYmyBGaFg?v2YyBgX7uJL{|7Lxk+x3AK1}drdTtl z>2&ycjnikqnQ#{QmYgH!i{NZH2hJtmk!$2SNo2N0tC?|HV-y3IvPi&1@C&#YE+H4l zMRJK;UI&-KegjXC+vNK~suG?iEyx`vUY8O$UrV;ypIw*j z_4ZtXwh0YR>EuWI=lgIl6&Mnc|fYjkK`fwi98~Y$rJLFJlg_q z!|&l8_yeVf_uzf_09L^t;Y0Wnc}`xCYVww?l<#Rk(E!kZOM_542=HhyutXTs7xa4t z-_)$(HTk&!R+C?9*1&6(lH6rvR%UKWX2#gW+>BwPYV#3MprQ}~1j$SC>O)4MAk>ge zeH4sBPy_NS`HlQugc?!#s4@A2W#dn#Oz%mZ8CSc|zt)L_DC}<{QdUGxUb7-^h_`Pj zH?dXgRvGy05P3_O9D~+L48ET4DEcb1wO-RjT8lq(6T`lcMgS0)9x+D%y z&luM+Eq8Dsiefe!nyPdtrmn19MMe~h;wUaMAv3ZdE3zRwav&%3%rG>x0SyohKB57K z29O3JOs3J<+|AZZp(izf0RKP%gu(xaBCiRo4I4>=hBRnI zD5gp9bc{NqF3h`FF)Ncbq5)1rPTjd>hr0R0)M$reaQ8Fj7*rRrju$yRzaga-Wo3Ft7C%hn?_ z9F0IDQ8vm!qi7(Yfsh6w8i;8ip+VRNG#ZUTW7%^aWuSqS1~MA7s2yamiJX|)Iz2OY zL{5hh=@~3fbzhRx`ZM2|`iD(LGuXsH)6jGp$Z4P`L^CNp4V26$X0v`?jlbv9Ae_i5 zQ9feOBJ>68w8dx%T8f&ZOXoJ==>&-g?K{SY@ftChMXwbYS2&}1^e-Csa zD*7wX8)=}XLBzjASyktTO*Lx2q(RgF2y=Z@1%dZF=V5~64QvOuyjb>hmKSB zO=v$lfDWQV=rH;k9YIIYF&aeCpcxIKX`rJ)3=P<&vK}+iAa)b_hB8nFbP6;;XV~kv zR6Y&jSofJ|U?FUD6KY02wvVxcP8Bs4R?@&kWOXLrpc2=in=Fg$K?8FQeC&zsBEZ1< zJiCXg*wz8vM-OOVrGc#w{fHjYz)k}v@iLcqO6DW6E(JYBFKRmz^qlx<;9yG?+nTf> zGym3N)X)1ndR>#hf1p3nD;mVpz(oUh5qg8F!Dt77vG?pPS4NPZ`f9a6x?HP=Rvhl&0n5_@pXwbb7XW?Nq=s|XfT!r<4QUw6o7#@F#GZ^z&B|yz5w5*!G!1Pu z8U#MnZu}#D{GV@D{DjKK&uB1-w57phriWkA^db%vZ!4DE zwzwT`kCSjm+?m}brm$PYRCasV2j}6%cn`i66ci*0G6b~?>Jv0B=<}e%L05uq1>Fy- z3VImyDCkKb=v6QX{y3N$%nKF-FA822TpYYJcyI8b;FH0n!B>K>23Lj*4jCOXHY6`( z0*A+uaa5crj+Nu)csV{!b52`MJ5GB}5~m}Fa1L{-I4?QBbN=MK;k@O%=QieYxS?D= zw~)J?dxCq4dxrZh_dNFk_Y$|1dxd+ITgiRO4Ls-m%zerIjr#}p755GIE%$vW2t}b0 z?EY~;=(NyHp%+76^7uRxFNxQUm&)tK>%$ww8_CPz=Plwb z=B?nZ;;rEo@J{f4;63C0#`}Z!iuZ;O`IsNfZ@_QFx9~Ig)A{rHEBV{`JNXCr#{&FQ z{EPgX{OA0i`L6{?5GoJ}RDvi$v>-+hCol`F0=uBKppzh3&|A<)&`*#l$P(lUCJTsQ zieQFdmSDDEu7D8~3W@|<1jhwu1y=>v1UCe?1$P8@1*qOU}|M0-U0LEM5qHF=)G7ajul(QcCl0J75l`^ z#Vy61#off|;-TVU;t}F(@i_5#@kH@tF%fSP-w;kizv8&!gyNLqjN)6xdBp=IrIafJNy=np4`okf zx^k#;m~w%l}nY&l`EAel`q4agf|U0hTFm&;ql?_@D|~% z!rO$m4R06TDLgg2zbacbN;OgSxoV+mscOCIE7dO59@Re80o5VZ*Q#@>?^G96msMq| za#e-un(Ccet?r;6t0n>U6!mO1qh6_At=_2KrY=z*S6@`$QQuYHS68VYsvoJJsGq4{ zM1n{Z859{3*)Z~>$d4nrk-SJjWK5(ha!4c-xjXWA4PRr>w9|Cc4AczP4AEq1hHFM@ zax}S`&ot9CGc>a_vo&)y^EC@KjAnyozves59nG)W5UofXP-&yJFK>IE)hlXj6p30DwI-^d*~Mm+(IC1(v?+RV^#15m(PyL2MPG=%6kQtqT8DI@ zI+0GIlLmC*x(J<07pY6q<>)5pCh8{ZX6P2_w(7R)cIrxWyLEeY`*jC(hjmAE$8_K5 zPU>#!?&$97?(3>_4|R`pPjxSJFLl4`Ug@fJ?_!$9*kb%K17a4$6vpg}xvdBKP<=Cf zg1(EspMHQoLqAwQM4zQ6`dRvU`UU!Y{UUv!Kwqr?QolvNO}|^eSHE9>P=8o|PG6~i ztp82_&hU|eZx9;9hA=}DLsNs=pfN-l;teeftqh$E8HRC&@rKU~^9_p(YYm$X`waUH z2MmV{<%WlbKa5qI&$!UY7}pv%8+RIa8TT0X2aHFJ zXN?byPh%U$iego<##m>pJ2oNKAA2nJ+t@p?4`ScM;W%y_KTZ@kKW=HA@7df3LwU4!rx6}5i_8InB_SyDb z_9{n+BM|CPI^rB=huhKC(bLh}(btjX$aai!jB!kGOmYMqv}1u|nPZ1zx8uB{+)?Sc z;kf0fasnrIhB%u!W1L2($!T>uoGz!w>2r2>_H_1fra9A{gPcR0qnu-$dCm#Wnak;g;P?jdjp7@} zbK`mOg7~=jg|aP@bky9T-jyN0;3TzRg^F4{HK zHOn=}HQ%++waazcb=&p8_0aX$_008~>rdAk*IUVEG2+5OT3JzS5_Blbu=;hqSOirv&$JfC`!Jz1V?&n(XhPqAmG zXP0NM=YZ$1=alEH=e*~F=d!2FbJJ7h`PtjhEA}?=s=OL+GjEL7>a}~FUYFP7P4M>e z=6P3ocYBX}%e+?u-fP~Q-tWD4y$`$(y^p=s-ggO90!+XO!3hl#8YMJN;3RkwvJ#dh zoJn};Q~SF5rur887WtO=mit!u*7!<%hkVC;Cw!-UXMIG%J@P&EJ@@_M t`_=c>AM6+SWqyS}+#lhO^6UJ1f2`l+ch~(V1*NcVfZx?!>(BmH{{#*161@Nb delta 6688 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 h;h*JS;9ult{F Date: Wed, 9 Jul 2025 20:47:37 +0100 Subject: [PATCH 3/4] adds tennis 2 simplified --- .../UserInterfaceState.xcuserstate | Bin 19040 -> 17904 bytes .../swift/Tennis2.playground/Contents.swift | 226 ++++++++++++++++++ .../Tennis2.playground/contents.xcplayground | 2 + .../contents.xcworkspacedata | 7 + .../UserInterfaceState.xcuserstate | Bin 0 -> 14035 bytes 5 files changed, 235 insertions(+) create mode 100644 examples/swift/Tennis2.playground/Contents.swift create mode 100644 examples/swift/Tennis2.playground/contents.xcplayground create mode 100644 examples/swift/Tennis2.playground/playground.xcworkspace/contents.xcworkspacedata create mode 100644 examples/swift/Tennis2.playground/playground.xcworkspace/xcuserdata/henryallsuch.xcuserdatad/UserInterfaceState.xcuserstate diff --git a/examples/swift/Tennis.playground/playground.xcworkspace/xcuserdata/henryallsuch.xcuserdatad/UserInterfaceState.xcuserstate b/examples/swift/Tennis.playground/playground.xcworkspace/xcuserdata/henryallsuch.xcuserdatad/UserInterfaceState.xcuserstate index 0041a05e5399033bfb139ac7407cd0136a2ab878..82621fc8089f0f48f26e8e4852b77e4239b3511a 100644 GIT binary patch delta 5519 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 A_5c6? delta 6115 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 BhUNeO diff --git a/examples/swift/Tennis2.playground/Contents.swift b/examples/swift/Tennis2.playground/Contents.swift new file mode 100644 index 0000000..87b0e59 --- /dev/null +++ b/examples/swift/Tennis2.playground/Contents.swift @@ -0,0 +1,226 @@ +import UIKit + +// Tennis game states +enum GameState { + case playing + case deuce + case won +} + +// Tennis points: 0, 15, 30, 40, advantage +enum Points { + case love // 0 points + case fifteen // 15 points + case thirty // 30 points + case forty // 40 points + case advantage // advantage (after deuce) +} + +// A tennis player with their current points +struct Player { + let points: Points + + init(points: Points = .love) { + self.points = points + } +} + +// The tennis game +struct Game { + let state: GameState + let player1: Player + let player2: Player + + init(state: GameState = .playing, player1: Player = Player(), player2: Player = Player()) { + self.state = state + self.player1 = player1 + self.player2 = player2 + } +} + +// Simple tennis scoring +struct TennisScorer { + + // Award a point to player 1 or player 2 + static func awardPoint(to playerNumber: Int, game: Game) -> Game { + + if playerNumber == 1 { + let newPlayer1 = getNextPoints(from: game.player1.points) + let updatedGame = Game(state: game.state, + player1: Player(points: newPlayer1), + player2: game.player2) + return checkGameResult(updatedGame, whoScored: 1) + } else { + let newPlayer2 = getNextPoints(from: game.player2.points) + let updatedGame = Game(state: game.state, + player1: game.player1, + player2: Player(points: newPlayer2)) + return checkGameResult(updatedGame, whoScored: 2) + } + } + + // What happens when you score a point? + static func getNextPoints(from currentPoints: Points) -> Points { + switch currentPoints { + case .love: return .fifteen + case .fifteen: return .thirty + case .thirty: return .forty + case .forty: return .advantage + case .advantage: return .advantage // Stay at advantage until you win + } + } + + // Check if someone won or if it's deuce + static func checkGameResult(_ game: Game, whoScored: Int) -> Game { + let p1 = game.player1.points + let p2 = game.player2.points + + // Both have 40? That's deuce! + if p1 == .forty && p2 == .forty { + return Game(state: .deuce, player1: game.player1, player2: game.player2) + } + + // Someone with advantage wins the game + if (whoScored == 1 && p1 == .advantage && p2 != .forty) || + (whoScored == 2 && p2 == .advantage && p1 != .forty) { + print("๐ŸŽพ Player \(whoScored) wins the game!") + return Game(state: .won, player1: game.player1, player2: game.player2) + } + + // Someone wins without deuce (opponent has less than 40) + if (whoScored == 1 && p1 == .advantage && p2 != .forty && p2 != .advantage) || + (whoScored == 2 && p2 == .advantage && p1 != .forty && p1 != .advantage) { + print("๐ŸŽพ Player \(whoScored) wins the game!") + return Game(state: .won, player1: game.player1, player2: game.player2) + } + + // Back to deuce from advantage + if game.state == .deuce && + ((whoScored == 1 && p2 == .advantage) || (whoScored == 2 && p1 == .advantage)) { + return Game(state: .deuce, + player1: Player(points: .forty), + player2: Player(points: .forty)) + } + + return Game(state: .playing, player1: game.player1, player2: game.player2) + } +} + +// Show the score in a friendly way +struct ScoreDisplay { + static func show(_ game: Game) { + let p1Score = friendlyScore(game.player1.points) + let p2Score = friendlyScore(game.player2.points) + + print("Score: \(p1Score) - \(p2Score)") + if game.state == .deuce { + print("It's DEUCE! ๐Ÿค") + } else if game.state == .won { + print("Game Over! ๐Ÿ†") + } + } + + static func friendlyScore(_ points: Points) -> String { + switch points { + case .love: return "0" + case .fifteen: return "15" + case .thirty: return "30" + case .forty: return "40" + case .advantage: return "ADV" + } + } +} + + +// ๐Ÿงช SIMPLE TESTS - Easy to read and understand! + +func testBasicScoring() { + print("\n๐Ÿธ Testing Basic Tennis Scoring...") + var game = Game() + + // Player 1 scores: 0 -> 15 + game = TennisScorer.awardPoint(to: 1, game: game) + ScoreDisplay.show(game) + checkEqual(game.player1.points, .fifteen, "Player 1 should have 15") + + // Player 1 scores again: 15 -> 30 + game = TennisScorer.awardPoint(to: 1, game: game) + ScoreDisplay.show(game) + checkEqual(game.player1.points, .thirty, "Player 1 should have 30") + + // Player 1 scores again: 30 -> 40 + game = TennisScorer.awardPoint(to: 1, game: game) + ScoreDisplay.show(game) + checkEqual(game.player1.points, .forty, "Player 1 should have 40") +} + +func testSimpleWin() { + print("\n๐Ÿธ Testing Simple Win...") + var game = Game() + + // Player 1 gets 40, Player 2 has 0 + game = Game(player1: Player(points: .forty), player2: Player(points: .love)) + + // Player 1 scores the winning point + game = TennisScorer.awardPoint(to: 1, game: game) + ScoreDisplay.show(game) + checkEqual(game.state, .won, "Game should be won") +} + +func testDeuce() { + print("\n๐Ÿธ Testing Deuce...") + var game = Game() + + // Both players get to 40 + game = Game(player1: Player(points: .forty), player2: Player(points: .forty)) + ScoreDisplay.show(game) + checkEqual(game.state, .deuce, "Should be deuce when both have 40") +} + +func testAdvantage() { + print("\n๐Ÿธ Testing Advantage...") + var game = Game() + + // Start at deuce + game = Game(state: .deuce, player1: Player(points: .forty), player2: Player(points: .forty)) + + // Player 1 scores from deuce + game = TennisScorer.awardPoint(to: 1, game: game) + ScoreDisplay.show(game) + checkEqual(game.player1.points, .advantage, "Player 1 should have advantage") +} + +func testWinFromAdvantage() { + print("\n๐Ÿธ Testing Win from Advantage...") + var game = Game() + + // Player 1 has advantage, Player 2 has 40 + game = Game(player1: Player(points: .advantage), player2: Player(points: .forty)) + + // Player 1 scores the winning point + game = TennisScorer.awardPoint(to: 1, game: game) + ScoreDisplay.show(game) + checkEqual(game.state, .won, "Player 1 should win the game") +} + +// ๐Ÿƒโ€โ™‚๏ธ Run all the simple tests +print("TENNIS GAME TESTS") +print("===================") + +testBasicScoring() +testSimpleWin() +testDeuce() +testAdvantage() +testWinFromAdvantage() + +print("\n All tests done!") + +// ๐Ÿ“‹ Simple test helper +func checkEqual(_ actual: T, _ expected: T, _ message: String = "") { + if actual == expected { + print(" โœ… \(message)") + } else { + print(" โŒ \(message)") + print(" Expected: \(expected), Got: \(actual)") + } +} diff --git a/examples/swift/Tennis2.playground/contents.xcplayground b/examples/swift/Tennis2.playground/contents.xcplayground new file mode 100644 index 0000000..7f43ce8 --- /dev/null +++ b/examples/swift/Tennis2.playground/contents.xcplayground @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/examples/swift/Tennis2.playground/playground.xcworkspace/contents.xcworkspacedata b/examples/swift/Tennis2.playground/playground.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..ca3329e --- /dev/null +++ b/examples/swift/Tennis2.playground/playground.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/examples/swift/Tennis2.playground/playground.xcworkspace/xcuserdata/henryallsuch.xcuserdatad/UserInterfaceState.xcuserstate b/examples/swift/Tennis2.playground/playground.xcworkspace/xcuserdata/henryallsuch.xcuserdatad/UserInterfaceState.xcuserstate new file mode 100644 index 0000000000000000000000000000000000000000..08bf2cf495a899c9abc3e4aa62a1ce54bfdb2903 GIT binary patch literal 14035 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 literal 0 HcmV?d00001 From 5f9b6a8fedc433f4b96172ba44110eb79e5baf5c Mon Sep 17 00:00:00 2001 From: Henry Allsuch Date: Thu, 10 Jul 2025 09:44:58 +0100 Subject: [PATCH 4/4] adds simplified v2 --- .../UserInterfaceState.xcuserstate | Bin 17904 -> 19030 bytes .../swift/Tennis2.playground/Contents.swift | 132 ++++++++++++++---- .../UserInterfaceState.xcuserstate | Bin 14035 -> 11360 bytes 3 files changed, 103 insertions(+), 29 deletions(-) diff --git a/examples/swift/Tennis.playground/playground.xcworkspace/xcuserdata/henryallsuch.xcuserdatad/UserInterfaceState.xcuserstate b/examples/swift/Tennis.playground/playground.xcworkspace/xcuserdata/henryallsuch.xcuserdatad/UserInterfaceState.xcuserstate index 82621fc8089f0f48f26e8e4852b77e4239b3511a..095b0151f1115cc45ac7eb4773f0d5d56c599f25 100644 GIT binary patch delta 6654 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 diff --git a/examples/swift/Tennis2.playground/Contents.swift b/examples/swift/Tennis2.playground/Contents.swift index 87b0e59..cde8ff8 100644 --- a/examples/swift/Tennis2.playground/Contents.swift +++ b/examples/swift/Tennis2.playground/Contents.swift @@ -40,21 +40,20 @@ struct Game { // Simple tennis scoring struct TennisScorer { - + // Award a point to player 1 or player 2 static func awardPoint(to playerNumber: Int, game: Game) -> Game { + // Give the point to the player and update their score if playerNumber == 1 { - let newPlayer1 = getNextPoints(from: game.player1.points) - let updatedGame = Game(state: game.state, - player1: Player(points: newPlayer1), - player2: game.player2) + let newPoints = getNextPoints(from: game.player1.points) + let newPlayer1 = Player(points: newPoints) + let updatedGame = Game(state: game.state, player1: newPlayer1, player2: game.player2) return checkGameResult(updatedGame, whoScored: 1) } else { - let newPlayer2 = getNextPoints(from: game.player2.points) - let updatedGame = Game(state: game.state, - player1: game.player1, - player2: Player(points: newPlayer2)) + let newPoints = getNextPoints(from: game.player2.points) + let newPlayer2 = Player(points: newPoints) + let updatedGame = Game(state: game.state, player1: game.player1, player2: newPlayer2) return checkGameResult(updatedGame, whoScored: 2) } } @@ -72,38 +71,64 @@ struct TennisScorer { // Check if someone won or if it's deuce static func checkGameResult(_ game: Game, whoScored: Int) -> Game { - let p1 = game.player1.points - let p2 = game.player2.points - - // Both have 40? That's deuce! - if p1 == .forty && p2 == .forty { - return Game(state: .deuce, player1: game.player1, player2: game.player2) - } + let p1Points = game.player1.points + let p2Points = game.player2.points - // Someone with advantage wins the game - if (whoScored == 1 && p1 == .advantage && p2 != .forty) || - (whoScored == 2 && p2 == .advantage && p1 != .forty) { + // RULE 1: Win from advantage + // If you have advantage and score again, you win! + if didPlayerWin(whoScored: whoScored, p1Points: p1Points, p2Points: p2Points) { print("๐ŸŽพ Player \(whoScored) wins the game!") return Game(state: .won, player1: game.player1, player2: game.player2) } - // Someone wins without deuce (opponent has less than 40) - if (whoScored == 1 && p1 == .advantage && p2 != .forty && p2 != .advantage) || - (whoScored == 2 && p2 == .advantage && p1 != .forty && p1 != .advantage) { - print("๐ŸŽพ Player \(whoScored) wins the game!") - return Game(state: .won, player1: game.player1, player2: game.player2) + // RULE 2: Check for deuce + // When both players have 40, it's deuce + if p1Points == .forty && p2Points == .forty { + return Game(state: .deuce, player1: game.player1, player2: game.player2) } - // Back to deuce from advantage - if game.state == .deuce && - ((whoScored == 1 && p2 == .advantage) || (whoScored == 2 && p1 == .advantage)) { + // RULE 3: Back to deuce from advantage + // If one player has advantage but the other scores, back to deuce + if shouldGoBackToDeuce(currentState: game.state, whoScored: whoScored, p1Points: p1Points, p2Points: p2Points) { return Game(state: .deuce, player1: Player(points: .forty), player2: Player(points: .forty)) } + // Continue playing return Game(state: .playing, player1: game.player1, player2: game.player2) } + + // Helper: Did someone win the game? + static func didPlayerWin(whoScored: Int, p1Points: Points, p2Points: Points) -> Bool { + if whoScored == 1 && p1Points == .advantage { + return true + } + if whoScored == 2 && p2Points == .advantage { + return true + } + return false + } + + // Helper: Should we go back to deuce? + static func shouldGoBackToDeuce(currentState: GameState, whoScored: Int, p1Points: Points, p2Points: Points) -> Bool { + // Only relevant if we're already at deuce + if currentState != .deuce { + return false + } + + // Player 1 scored, but Player 2 had advantage + if whoScored == 1 && p2Points == .advantage { + return true + } + + // Player 2 scored, but Player 1 had advantage + if whoScored == 2 && p1Points == .advantage { + return true + } + + return false + } } // Show the score in a friendly way @@ -171,8 +196,13 @@ func testDeuce() { print("\n๐Ÿธ Testing Deuce...") var game = Game() - // Both players get to 40 - game = Game(player1: Player(points: .forty), player2: Player(points: .forty)) + // Get both players to 30, then they both score to reach 40-40 (deuce) + game = Game(player1: Player(points: .thirty), player2: Player(points: .thirty)) + + // Player 1 scores: 30 -> 40 + game = TennisScorer.awardPoint(to: 1, game: game) + // Player 2 scores: 30 -> 40 (this should trigger deuce) + game = TennisScorer.awardPoint(to: 2, game: game) ScoreDisplay.show(game) checkEqual(game.state, .deuce, "Should be deuce when both have 40") } @@ -215,6 +245,50 @@ testWinFromAdvantage() print("\n All tests done!") +// ๐ŸŽพ Let's play a quick demo game! +print("\n\n๐ŸŽพ DEMO GAME - Watch a complete tennis game!") +print("===============================================") + +var demoGame = Game() +print("Starting game...") +ScoreDisplay.show(demoGame) + +// Player 1 scores some points +print("\nPlayer 1 scores...") +demoGame = TennisScorer.awardPoint(to: 1, game: demoGame) +ScoreDisplay.show(demoGame) + +print("\nPlayer 1 scores again...") +demoGame = TennisScorer.awardPoint(to: 1, game: demoGame) +ScoreDisplay.show(demoGame) + +// Player 2 catches up +print("\nPlayer 2 scores...") +demoGame = TennisScorer.awardPoint(to: 2, game: demoGame) +ScoreDisplay.show(demoGame) + +print("\nPlayer 2 scores again...") +demoGame = TennisScorer.awardPoint(to: 2, game: demoGame) +ScoreDisplay.show(demoGame) + +// Both reach 40 - deuce! +print("\nPlayer 1 scores (gets to 40)...") +demoGame = TennisScorer.awardPoint(to: 1, game: demoGame) +ScoreDisplay.show(demoGame) + +print("\nPlayer 2 scores (also gets to 40 - DEUCE!)...") +demoGame = TennisScorer.awardPoint(to: 2, game: demoGame) +ScoreDisplay.show(demoGame) + +// Advantage and win +print("\nPlayer 1 scores from deuce (gets advantage)...") +demoGame = TennisScorer.awardPoint(to: 1, game: demoGame) +ScoreDisplay.show(demoGame) + +print("\nPlayer 1 scores again (WINS!)...") +demoGame = TennisScorer.awardPoint(to: 1, game: demoGame) +ScoreDisplay.show(demoGame) + // ๐Ÿ“‹ Simple test helper func checkEqual(_ actual: T, _ expected: T, _ message: String = "") { if actual == expected { diff --git a/examples/swift/Tennis2.playground/playground.xcworkspace/xcuserdata/henryallsuch.xcuserdatad/UserInterfaceState.xcuserstate b/examples/swift/Tennis2.playground/playground.xcworkspace/xcuserdata/henryallsuch.xcuserdatad/UserInterfaceState.xcuserstate index 08bf2cf495a899c9abc3e4aa62a1ce54bfdb2903..27c864e80f109bea5445bbd2d1a62cdbd0f7cbc4 100644 GIT binary patch delta 4052 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 delta 5729 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 fyhFWyZ?PA9$9pH$Y=$7LX(dceOV>0M<(>CG)4