From c4ea985e00bb8dff3abfdbf542a9596c087fc454 Mon Sep 17 00:00:00 2001 From: hsw1920 Date: Mon, 18 Mar 2024 19:55:45 +0900 Subject: [PATCH 1/8] =?UTF-8?q?Step1:=20Storyboard=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../project.pbxproj | 14 --------- .../Base.lproj/Main.storyboard | 30 ------------------- .../RockPaperScissors/Info.plist | 2 -- .../RockPaperScissors/SceneDelegate.swift | 8 ++--- 4 files changed, 4 insertions(+), 50 deletions(-) delete mode 100644 RockPaperScissors/RockPaperScissors/Base.lproj/Main.storyboard diff --git a/RockPaperScissors/RockPaperScissors.xcodeproj/project.pbxproj b/RockPaperScissors/RockPaperScissors.xcodeproj/project.pbxproj index 0e9f7f1..08ee6cd 100644 --- a/RockPaperScissors/RockPaperScissors.xcodeproj/project.pbxproj +++ b/RockPaperScissors/RockPaperScissors.xcodeproj/project.pbxproj @@ -10,7 +10,6 @@ C784841D2B5E48E300FBF8B4 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = C784841C2B5E48E300FBF8B4 /* AppDelegate.swift */; }; C784841F2B5E48E300FBF8B4 /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = C784841E2B5E48E300FBF8B4 /* SceneDelegate.swift */; }; C78484212B5E48E300FBF8B4 /* GameViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C78484202B5E48E300FBF8B4 /* GameViewController.swift */; }; - C78484242B5E48E300FBF8B4 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = C78484222B5E48E300FBF8B4 /* Main.storyboard */; }; C78484262B5E48E400FBF8B4 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = C78484252B5E48E400FBF8B4 /* Assets.xcassets */; }; C78484292B5E48E400FBF8B4 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = C78484272B5E48E400FBF8B4 /* LaunchScreen.storyboard */; }; C78484642B6A32E500FBF8B4 /* GameView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C78484632B6A32E500FBF8B4 /* GameView.swift */; }; @@ -21,7 +20,6 @@ C784841C2B5E48E300FBF8B4 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; C784841E2B5E48E300FBF8B4 /* SceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SceneDelegate.swift; sourceTree = ""; }; C78484202B5E48E300FBF8B4 /* GameViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GameViewController.swift; sourceTree = ""; }; - C78484232B5E48E300FBF8B4 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; C78484252B5E48E400FBF8B4 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; C78484282B5E48E400FBF8B4 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; C784842A2B5E48E400FBF8B4 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; @@ -62,7 +60,6 @@ C784841E2B5E48E300FBF8B4 /* SceneDelegate.swift */, C78484202B5E48E300FBF8B4 /* GameViewController.swift */, C78484632B6A32E500FBF8B4 /* GameView.swift */, - C78484222B5E48E300FBF8B4 /* Main.storyboard */, C78484252B5E48E400FBF8B4 /* Assets.xcassets */, C78484272B5E48E400FBF8B4 /* LaunchScreen.storyboard */, C784842A2B5E48E400FBF8B4 /* Info.plist */, @@ -130,7 +127,6 @@ files = ( C78484292B5E48E400FBF8B4 /* LaunchScreen.storyboard in Resources */, C78484262B5E48E400FBF8B4 /* Assets.xcassets in Resources */, - C78484242B5E48E300FBF8B4 /* Main.storyboard in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -151,14 +147,6 @@ /* End PBXSourcesBuildPhase section */ /* Begin PBXVariantGroup section */ - C78484222B5E48E300FBF8B4 /* Main.storyboard */ = { - isa = PBXVariantGroup; - children = ( - C78484232B5E48E300FBF8B4 /* Base */, - ); - name = Main.storyboard; - sourceTree = ""; - }; C78484272B5E48E400FBF8B4 /* LaunchScreen.storyboard */ = { isa = PBXVariantGroup; children = ( @@ -300,7 +288,6 @@ INFOPLIST_FILE = RockPaperScissors/Info.plist; INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; INFOPLIST_KEY_UILaunchStoryboardName = LaunchScreen; - INFOPLIST_KEY_UIMainStoryboardFile = Main; INFOPLIST_KEY_UISupportedInterfaceOrientations = "UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", @@ -326,7 +313,6 @@ INFOPLIST_FILE = RockPaperScissors/Info.plist; INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; INFOPLIST_KEY_UILaunchStoryboardName = LaunchScreen; - INFOPLIST_KEY_UIMainStoryboardFile = Main; INFOPLIST_KEY_UISupportedInterfaceOrientations = "UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", diff --git a/RockPaperScissors/RockPaperScissors/Base.lproj/Main.storyboard b/RockPaperScissors/RockPaperScissors/Base.lproj/Main.storyboard deleted file mode 100644 index 97b58be..0000000 --- a/RockPaperScissors/RockPaperScissors/Base.lproj/Main.storyboard +++ /dev/null @@ -1,30 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/RockPaperScissors/RockPaperScissors/Info.plist b/RockPaperScissors/RockPaperScissors/Info.plist index dd3c9af..0eb786d 100644 --- a/RockPaperScissors/RockPaperScissors/Info.plist +++ b/RockPaperScissors/RockPaperScissors/Info.plist @@ -15,8 +15,6 @@ Default Configuration UISceneDelegateClassName $(PRODUCT_MODULE_NAME).SceneDelegate - UISceneStoryboardFile - Main diff --git a/RockPaperScissors/RockPaperScissors/SceneDelegate.swift b/RockPaperScissors/RockPaperScissors/SceneDelegate.swift index b3f48cf..bf9d7ef 100644 --- a/RockPaperScissors/RockPaperScissors/SceneDelegate.swift +++ b/RockPaperScissors/RockPaperScissors/SceneDelegate.swift @@ -12,10 +12,10 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate { func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) { - // Use this method to optionally configure and attach the UIWindow `window` to the provided UIWindowScene `scene`. - // If using a storyboard, the `window` property will automatically be initialized and attached to the scene. - // This delegate does not imply the connecting scene or session are new (see `application:configurationForConnectingSceneSession` instead). - guard let _ = (scene as? UIWindowScene) else { return } + guard let windowScene = (scene as? UIWindowScene) else { return } + window = UIWindow(windowScene: windowScene) + window?.rootViewController = GameViewController() + window?.makeKeyAndVisible() } func sceneDidDisconnect(_ scene: UIScene) { From b6bb656351a5ee7ad33cf8598485c87725679b83 Mon Sep 17 00:00:00 2001 From: hsw1920 Date: Tue, 19 Mar 2024 15:23:07 +0900 Subject: [PATCH 2/8] =?UTF-8?q?Step1:=20Testcode=20=ED=85=8C=EC=8A=A4?= =?UTF-8?q?=ED=8C=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../project.pbxproj | 129 +++++++++++++++++- .../xcschemes/RockPaperScissors.xcscheme | 90 ++++++++++++ .../RockPaperScissors/Game.swift | 43 ++++++ .../RockPaperScissorsTests.swift | 40 ++++++ 4 files changed, 301 insertions(+), 1 deletion(-) create mode 100644 RockPaperScissors/RockPaperScissors.xcodeproj/xcshareddata/xcschemes/RockPaperScissors.xcscheme create mode 100644 RockPaperScissors/RockPaperScissors/Game.swift create mode 100644 RockPaperScissors/RockPaperScissorsTests/RockPaperScissorsTests.swift diff --git a/RockPaperScissors/RockPaperScissors.xcodeproj/project.pbxproj b/RockPaperScissors/RockPaperScissors.xcodeproj/project.pbxproj index 08ee6cd..e8962fa 100644 --- a/RockPaperScissors/RockPaperScissors.xcodeproj/project.pbxproj +++ b/RockPaperScissors/RockPaperScissors.xcodeproj/project.pbxproj @@ -7,6 +7,8 @@ objects = { /* Begin PBXBuildFile section */ + 60CE25AA2BA962B100785863 /* RockPaperScissorsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 60CE25A92BA962B100785863 /* RockPaperScissorsTests.swift */; }; + 60CE25B12BA9652800785863 /* Game.swift in Sources */ = {isa = PBXBuildFile; fileRef = 60CE25B02BA9652800785863 /* Game.swift */; }; C784841D2B5E48E300FBF8B4 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = C784841C2B5E48E300FBF8B4 /* AppDelegate.swift */; }; C784841F2B5E48E300FBF8B4 /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = C784841E2B5E48E300FBF8B4 /* SceneDelegate.swift */; }; C78484212B5E48E300FBF8B4 /* GameViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C78484202B5E48E300FBF8B4 /* GameViewController.swift */; }; @@ -15,7 +17,20 @@ C78484642B6A32E500FBF8B4 /* GameView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C78484632B6A32E500FBF8B4 /* GameView.swift */; }; /* End PBXBuildFile section */ +/* Begin PBXContainerItemProxy section */ + 60CE25AB2BA962B100785863 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = C78484112B5E48E300FBF8B4 /* Project object */; + proxyType = 1; + remoteGlobalIDString = C78484182B5E48E300FBF8B4; + remoteInfo = RockPaperScissors; + }; +/* End PBXContainerItemProxy section */ + /* Begin PBXFileReference section */ + 60CE25A72BA962B100785863 /* RockPaperScissorsTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RockPaperScissorsTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + 60CE25A92BA962B100785863 /* RockPaperScissorsTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RockPaperScissorsTests.swift; sourceTree = ""; }; + 60CE25B02BA9652800785863 /* Game.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Game.swift; sourceTree = ""; }; C78484192B5E48E300FBF8B4 /* RockPaperScissors.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = RockPaperScissors.app; sourceTree = BUILT_PRODUCTS_DIR; }; C784841C2B5E48E300FBF8B4 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; C784841E2B5E48E300FBF8B4 /* SceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SceneDelegate.swift; sourceTree = ""; }; @@ -27,6 +42,13 @@ /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ + 60CE25A42BA962B100785863 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; C78484162B5E48E300FBF8B4 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; @@ -37,10 +59,19 @@ /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ + 60CE25A82BA962B100785863 /* RockPaperScissorsTests */ = { + isa = PBXGroup; + children = ( + 60CE25A92BA962B100785863 /* RockPaperScissorsTests.swift */, + ); + path = RockPaperScissorsTests; + sourceTree = ""; + }; C78484102B5E48E300FBF8B4 = { isa = PBXGroup; children = ( C784841B2B5E48E300FBF8B4 /* RockPaperScissors */, + 60CE25A82BA962B100785863 /* RockPaperScissorsTests */, C784841A2B5E48E300FBF8B4 /* Products */, ); sourceTree = ""; @@ -49,6 +80,7 @@ isa = PBXGroup; children = ( C78484192B5E48E300FBF8B4 /* RockPaperScissors.app */, + 60CE25A72BA962B100785863 /* RockPaperScissorsTests.xctest */, ); name = Products; sourceTree = ""; @@ -60,6 +92,7 @@ C784841E2B5E48E300FBF8B4 /* SceneDelegate.swift */, C78484202B5E48E300FBF8B4 /* GameViewController.swift */, C78484632B6A32E500FBF8B4 /* GameView.swift */, + 60CE25B02BA9652800785863 /* Game.swift */, C78484252B5E48E400FBF8B4 /* Assets.xcassets */, C78484272B5E48E400FBF8B4 /* LaunchScreen.storyboard */, C784842A2B5E48E400FBF8B4 /* Info.plist */, @@ -70,6 +103,24 @@ /* End PBXGroup section */ /* Begin PBXNativeTarget section */ + 60CE25A62BA962B100785863 /* RockPaperScissorsTests */ = { + isa = PBXNativeTarget; + buildConfigurationList = 60CE25AD2BA962B100785863 /* Build configuration list for PBXNativeTarget "RockPaperScissorsTests" */; + buildPhases = ( + 60CE25A32BA962B100785863 /* Sources */, + 60CE25A42BA962B100785863 /* Frameworks */, + 60CE25A52BA962B100785863 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + 60CE25AC2BA962B100785863 /* PBXTargetDependency */, + ); + name = RockPaperScissorsTests; + productName = RockPaperScissorsTests; + productReference = 60CE25A72BA962B100785863 /* RockPaperScissorsTests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; C78484182B5E48E300FBF8B4 /* RockPaperScissors */ = { isa = PBXNativeTarget; buildConfigurationList = C784842D2B5E48E400FBF8B4 /* Build configuration list for PBXNativeTarget "RockPaperScissors" */; @@ -94,9 +145,13 @@ isa = PBXProject; attributes = { BuildIndependentTargetsInParallel = 1; - LastSwiftUpdateCheck = 1500; + LastSwiftUpdateCheck = 1520; LastUpgradeCheck = 1500; TargetAttributes = { + 60CE25A62BA962B100785863 = { + CreatedOnToolsVersion = 15.2; + TestTargetID = C78484182B5E48E300FBF8B4; + }; C78484182B5E48E300FBF8B4 = { CreatedOnToolsVersion = 15.0.1; }; @@ -116,11 +171,19 @@ projectRoot = ""; targets = ( C78484182B5E48E300FBF8B4 /* RockPaperScissors */, + 60CE25A62BA962B100785863 /* RockPaperScissorsTests */, ); }; /* End PBXProject section */ /* Begin PBXResourcesBuildPhase section */ + 60CE25A52BA962B100785863 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; C78484172B5E48E300FBF8B4 /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; @@ -133,11 +196,20 @@ /* End PBXResourcesBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ + 60CE25A32BA962B100785863 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 60CE25AA2BA962B100785863 /* RockPaperScissorsTests.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; C78484152B5E48E300FBF8B4 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( C78484212B5E48E300FBF8B4 /* GameViewController.swift in Sources */, + 60CE25B12BA9652800785863 /* Game.swift in Sources */, C784841D2B5E48E300FBF8B4 /* AppDelegate.swift in Sources */, C78484642B6A32E500FBF8B4 /* GameView.swift in Sources */, C784841F2B5E48E300FBF8B4 /* SceneDelegate.swift in Sources */, @@ -146,6 +218,14 @@ }; /* End PBXSourcesBuildPhase section */ +/* Begin PBXTargetDependency section */ + 60CE25AC2BA962B100785863 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = C78484182B5E48E300FBF8B4 /* RockPaperScissors */; + targetProxy = 60CE25AB2BA962B100785863 /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + /* Begin PBXVariantGroup section */ C78484272B5E48E400FBF8B4 /* LaunchScreen.storyboard */ = { isa = PBXVariantGroup; @@ -158,6 +238,44 @@ /* End PBXVariantGroup section */ /* Begin XCBuildConfiguration section */ + 60CE25AE2BA962B100785863 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = 3NP4Z3WZ94; + GENERATE_INFOPLIST_FILE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 17.2; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.seuhong.RockPaperScissorsTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_EMIT_LOC_STRINGS = NO; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/RockPaperScissors.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/RockPaperScissors"; + }; + name = Debug; + }; + 60CE25AF2BA962B100785863 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = 3NP4Z3WZ94; + GENERATE_INFOPLIST_FILE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 17.2; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.seuhong.RockPaperScissorsTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_EMIT_LOC_STRINGS = NO; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/RockPaperScissors.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/RockPaperScissors"; + }; + name = Release; + }; C784842B2B5E48E400FBF8B4 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { @@ -330,6 +448,15 @@ /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ + 60CE25AD2BA962B100785863 /* Build configuration list for PBXNativeTarget "RockPaperScissorsTests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 60CE25AE2BA962B100785863 /* Debug */, + 60CE25AF2BA962B100785863 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; C78484142B5E48E300FBF8B4 /* Build configuration list for PBXProject "RockPaperScissors" */ = { isa = XCConfigurationList; buildConfigurations = ( diff --git a/RockPaperScissors/RockPaperScissors.xcodeproj/xcshareddata/xcschemes/RockPaperScissors.xcscheme b/RockPaperScissors/RockPaperScissors.xcodeproj/xcshareddata/xcschemes/RockPaperScissors.xcscheme new file mode 100644 index 0000000..a0f8dd3 --- /dev/null +++ b/RockPaperScissors/RockPaperScissors.xcodeproj/xcshareddata/xcschemes/RockPaperScissors.xcscheme @@ -0,0 +1,90 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/RockPaperScissors/RockPaperScissors/Game.swift b/RockPaperScissors/RockPaperScissors/Game.swift new file mode 100644 index 0000000..8f6264a --- /dev/null +++ b/RockPaperScissors/RockPaperScissors/Game.swift @@ -0,0 +1,43 @@ +// +// Game.swift +// RockPaperScissors +// +// Created by 홍승완 on 2024/03/19. +// + +import Foundation + +/// +/// 사용자와 컴퓨터의 가위 바위 보 게임입니다 +/// 사용자가 패를 선택하면 컴퓨터의 패는 임의의 패로 지정됩니다 +/// 현재 승/무/패 기록은 화면 중앙에 표시합니다 +/// 승/무/패 기록은 사용자 기준 승/무/패를 나타냅니다 +/// 삼세판 선승제로, 세 판을 먼저 이기는 쪽이 승리합니다 +/// 어느 한 쪽이 최종 승리하면 얼럿을 통해 승자를 표시하고 게임을 초기화합니다 +/// + +final class User { + var score: Int + + init(score: Int = 0) { + self.score = score + } +} + +final class Game { + let user: User + let computer: User + + init(user: User, computer: User) { + self.user = user + self.computer = computer + } + + func userWin(){ + user.score += 1 + } + + func computerWin(){ + user.score += 1 + } +} diff --git a/RockPaperScissors/RockPaperScissorsTests/RockPaperScissorsTests.swift b/RockPaperScissors/RockPaperScissorsTests/RockPaperScissorsTests.swift new file mode 100644 index 0000000..67c2cbe --- /dev/null +++ b/RockPaperScissors/RockPaperScissorsTests/RockPaperScissorsTests.swift @@ -0,0 +1,40 @@ +// +// RockPaperScissorsTests.swift +// RockPaperScissorsTests +// +// Created by 홍승완 on 2024/03/19. +// + +import XCTest +@testable import RockPaperScissors + +/// TDD의 RED-GREEN-REFACTOR 세 단계를 지켜가며 기능을 구현해봅니다 +/// UI와의 연동 없이 비지니스 로직만 구현합니다 +/// +/// 양쪽이 낸 패의 승패 판결을 위한 기능을 TDD로 구현합니다 +/// 해당 타입, 메서드를 구현해가며 지속적으로 리팩터링 합니다 +/// 삼세판을 이기면 승리하는 기능을 TDD로 구현합니다 +/// 삼세판이 끝나고 승패가 갈리면 초기화 하는 기능을 TDD로 구현합니다 +/// 성능에 유리한 코드로 작성하도록 노력합니다 +/// 기획의 변경에 대해서 최대한 열린 코드로 작성해봅니다 + +final class RockPaperScissorsTests: XCTestCase { + var sut: Game? + + override func setUpWithError() throws { + sut = Game(user: User(), computer: User()) + } + + // TestCode 테스팅 + func test_userWin() throws { + // given + let userScore = sut?.user.score + let computerScore = sut?.computer.score + // when + sut?.userWin() + + // then + XCTAssertEqual(sut?.user.score, 1) + } + +} From ba224cec1766a464577a2bf6efa8f96557ae10ea Mon Sep 17 00:00:00 2001 From: hsw1920 Date: Tue, 19 Mar 2024 17:26:08 +0900 Subject: [PATCH 3/8] =?UTF-8?q?Step1:=20=EC=82=BC=EC=84=B8=ED=8C=90,=20?= =?UTF-8?q?=EB=A6=AC=EC=85=8B=20=EB=93=B1=20testcode=20=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../RockPaperScissors/Game.swift | 121 +++++++++++++- .../RockPaperScissors/GameView.swift | 14 +- .../GameViewController.swift | 5 +- .../RockPaperScissorsTests.swift | 157 +++++++++++++++++- 4 files changed, 273 insertions(+), 24 deletions(-) diff --git a/RockPaperScissors/RockPaperScissors/Game.swift b/RockPaperScissors/RockPaperScissors/Game.swift index 8f6264a..94e8a15 100644 --- a/RockPaperScissors/RockPaperScissors/Game.swift +++ b/RockPaperScissors/RockPaperScissors/Game.swift @@ -16,11 +16,71 @@ import Foundation /// 어느 한 쪽이 최종 승리하면 얼럿을 통해 승자를 표시하고 게임을 초기화합니다 /// +struct Score { + var winCount: Int + var loseCount: Int + var drawCount: Int + + init(winCount: Int = 0, + loseCount: Int = 0, + drawCount: Int = 0) { + self.winCount = winCount + self.loseCount = loseCount + self.drawCount = drawCount + } + + mutating func win() { + self.winCount += 1 + } + mutating func lose() { + self.loseCount += 1 + } + mutating func draw() { + self.drawCount += 1 + } +} + +enum Hand: CaseIterable { + case paper, rock, scissor + + var description: String { + switch self { + case .paper: return "🖐️" + case .rock: return "✊" + case .scissor: return "✌️" + } + } +} + final class User { - var score: Int + var score: Score + var hand: Hand - init(score: Int = 0) { + init(score: Score = Score(), + hand: Hand) { self.score = score + self.hand = hand + } + + func winGame() { + score.win() + } + func loseGame() { + score.lose() + } + func drawGame() { + score.draw() + } + + func changeHand() { + let randomHand = Hand.allCases[Int.random(in: 0.. Bool { + return ((user.hand == .paper && + computer.hand == .rock) || + (user.hand == .rock && + computer.hand == .scissor) || + (user.hand == .scissor && + computer.hand == .paper)) + } + + func userLose() -> Bool { + return ((user.hand == .paper && + computer.hand == .scissor) || + (user.hand == .rock && + computer.hand == .paper) || + (user.hand == .scissor && + computer.hand == .rock)) + } + + func draw() -> Bool { + return !(userWin()||userLose()) } - func computerWin(){ - user.score += 1 + func resetGame() { + user.reset() + computer.reset() } } diff --git a/RockPaperScissors/RockPaperScissors/GameView.swift b/RockPaperScissors/RockPaperScissors/GameView.swift index f86b618..b239a3c 100644 --- a/RockPaperScissors/RockPaperScissors/GameView.swift +++ b/RockPaperScissors/RockPaperScissors/GameView.swift @@ -6,12 +6,6 @@ import UIKit -fileprivate enum Hand { - static let paper: String = "🖐️" - static let rock: String = "✊" - static let scissor: String = "✌️" -} - class GameView: UIView { private let computerHandLabel: UILabel = UILabel() @@ -19,6 +13,7 @@ class GameView: UIView { private let resultLabel: UILabel = UILabel() private let currentWinLoseLabel: UILabel = UILabel() + private let game: Game @objc private func touchUpNextButton() { @@ -31,8 +26,8 @@ class GameView: UIView { private func initialSetup() { backgroundColor = .white - computerHandLabel.text = Hand.paper - userHandLabel.text = Hand.paper + computerHandLabel.text = Hand.paper.description + userHandLabel.text = Hand.paper.description resultLabel.text = "이겼습니다!" currentWinLoseLabel.text = "0승 0무 0패" @@ -114,7 +109,8 @@ class GameView: UIView { ]) } - init() { + init(game: Game) { + self.game = game super.init(frame: .zero) initialSetup() layViews() diff --git a/RockPaperScissors/RockPaperScissors/GameViewController.swift b/RockPaperScissors/RockPaperScissors/GameViewController.swift index 401157a..41a2c8e 100644 --- a/RockPaperScissors/RockPaperScissors/GameViewController.swift +++ b/RockPaperScissors/RockPaperScissors/GameViewController.swift @@ -10,7 +10,10 @@ class GameViewController: UIViewController { override func loadView() { - view = GameView() + let randomUserHand = Hand.allCases[Int.random(in: 0..>>>>>>>>>>RANDOM TEST>>>>>>>>>>>>>>>>>>") + print("| userHand: \(userHand!)", "computerHand: \(computerHand!) |") + print("| user Win: \(win!) \t|\n", + "| user Lose: \(lose!)\t |\n", + "| user Draw:\(draw!)\t |") + print(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>") + } + + func test_nextGame(sut: Game) { + // given + let randomUserHand = Hand.allCases[Int.random(in: 0..>>>>>> USER WIN") + } else { + XCTAssertEqual(computerWin, 3) + print(">>>>>>> COMPUTER WIN") + } + } + + func test_resetGame() throws { + // given + try! test_threeGame() + + if sut!.user.score.winCount > sut!.computer.score.winCount { + XCTAssertEqual(sut?.user.score.winCount, 3) + } else { + XCTAssertEqual(sut?.computer.score.winCount, 3) + } + // when - sut?.userWin() + sut?.resetGame() // then - XCTAssertEqual(sut?.user.score, 1) + XCTAssertEqual(sut?.user.score.drawCount, 0) + XCTAssertEqual(sut?.user.score.winCount, 0) + XCTAssertEqual(sut?.user.score.loseCount, 0) + XCTAssertEqual(sut?.computer.score.drawCount, 0) + XCTAssertEqual(sut?.computer.score.winCount, 0) + XCTAssertEqual(sut?.computer.score.loseCount, 0) } } From 4de693b3bcc1dc18f6d06788bcf0dfd0dadc865d Mon Sep 17 00:00:00 2001 From: hsw1920 Date: Wed, 20 Mar 2024 19:11:58 +0900 Subject: [PATCH 4/8] =?UTF-8?q?Step1:=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20?= =?UTF-8?q?=EC=BD=94=EB=93=9C=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../RockPaperScissors/Game.swift | 34 ++--- .../RockPaperScissorsTests.swift | 131 ++++++++++++++++-- 2 files changed, 134 insertions(+), 31 deletions(-) diff --git a/RockPaperScissors/RockPaperScissors/Game.swift b/RockPaperScissors/RockPaperScissors/Game.swift index 94e8a15..8d78cf8 100644 --- a/RockPaperScissors/RockPaperScissors/Game.swift +++ b/RockPaperScissors/RockPaperScissors/Game.swift @@ -50,6 +50,17 @@ enum Hand: CaseIterable { case .scissor: return "✌️" } } + + var randomHand: Hand { + return random() + } + + private func random() -> Hand { + guard let randomUserHand = Hand.allCases.randomElement() else { + fatalError(">>> Error: random() in Hand") + } + return randomUserHand + } } final class User { @@ -73,14 +84,12 @@ final class User { } func changeHand() { - let randomHand = Hand.allCases[Int.random(in: 0..>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>") } + // MARK: nextGame 메서드 테스트 func test_nextGame(sut: Game) { // given let randomUserHand = Hand.allCases[Int.random(in: 0..>>>>>> USER WIN") - } else { + } + // Computer가 이김 + else { XCTAssertEqual(computerWin, 3) - print(">>>>>>> COMPUTER WIN") } } - func test_resetGame() throws { + // MARK: rest 메서드 테스트 + func test_reset() throws { // given try! test_threeGame() From c1eed0a1562144cfa6aac5c662b1062174002940 Mon Sep 17 00:00:00 2001 From: hsw1920 Date: Wed, 20 Mar 2024 19:15:07 +0900 Subject: [PATCH 5/8] =?UTF-8?q?Step1:=20View=EC=97=90=EC=84=9C=20=EB=B9=84?= =?UTF-8?q?=EC=A6=88=EB=8B=88=EC=8A=A4=20=EB=A1=9C=EC=A7=81=20=EB=B6=84?= =?UTF-8?q?=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- RockPaperScissors/RockPaperScissors/Game.swift | 4 ++++ RockPaperScissors/RockPaperScissors/GameView.swift | 5 ++--- RockPaperScissors/RockPaperScissors/GameViewController.swift | 3 +-- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/RockPaperScissors/RockPaperScissors/Game.swift b/RockPaperScissors/RockPaperScissors/Game.swift index 8d78cf8..6acbfdb 100644 --- a/RockPaperScissors/RockPaperScissors/Game.swift +++ b/RockPaperScissors/RockPaperScissors/Game.swift @@ -63,6 +63,10 @@ enum Hand: CaseIterable { } } +protocol Playable { + +} + final class User { var score: Score var hand: Hand diff --git a/RockPaperScissors/RockPaperScissors/GameView.swift b/RockPaperScissors/RockPaperScissors/GameView.swift index b239a3c..b477cf3 100644 --- a/RockPaperScissors/RockPaperScissors/GameView.swift +++ b/RockPaperScissors/RockPaperScissors/GameView.swift @@ -13,7 +13,7 @@ class GameView: UIView { private let resultLabel: UILabel = UILabel() private let currentWinLoseLabel: UILabel = UILabel() - private let game: Game + //private let game: Game @objc private func touchUpNextButton() { @@ -109,8 +109,7 @@ class GameView: UIView { ]) } - init(game: Game) { - self.game = game + init() { super.init(frame: .zero) initialSetup() layViews() diff --git a/RockPaperScissors/RockPaperScissors/GameViewController.swift b/RockPaperScissors/RockPaperScissors/GameViewController.swift index 41a2c8e..06232db 100644 --- a/RockPaperScissors/RockPaperScissors/GameViewController.swift +++ b/RockPaperScissors/RockPaperScissors/GameViewController.swift @@ -12,8 +12,7 @@ class GameViewController: UIViewController { override func loadView() { let randomUserHand = Hand.allCases[Int.random(in: 0.. Date: Wed, 20 Mar 2024 19:31:14 +0900 Subject: [PATCH 6/8] =?UTF-8?q?Step1:=20Score=20=EC=BA=A1=EC=8A=90?= =?UTF-8?q?=ED=99=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../RockPaperScissors/Game.swift | 77 +++++++++++++------ .../RockPaperScissorsTests.swift | 9 ++- 2 files changed, 59 insertions(+), 27 deletions(-) diff --git a/RockPaperScissors/RockPaperScissors/Game.swift b/RockPaperScissors/RockPaperScissors/Game.swift index 6acbfdb..24459c9 100644 --- a/RockPaperScissors/RockPaperScissors/Game.swift +++ b/RockPaperScissors/RockPaperScissors/Game.swift @@ -16,27 +16,48 @@ import Foundation /// 어느 한 쪽이 최종 승리하면 얼럿을 통해 승자를 표시하고 게임을 초기화합니다 /// -struct Score { - var winCount: Int - var loseCount: Int - var drawCount: Int +protocol GameScore { + var winCount: Int { get } + var loseCount: Int { get } + var drawCount: Int { get } + mutating func win() + mutating func lose() + mutating func draw() +} + +struct Score: GameScore { + private var _winCount: Int + private var _loseCount: Int + private var _drawCount: Int + + var winCount: Int { + return _winCount + } + + var loseCount: Int { + return _loseCount + } + + var drawCount: Int { + return _drawCount + } - init(winCount: Int = 0, - loseCount: Int = 0, - drawCount: Int = 0) { - self.winCount = winCount - self.loseCount = loseCount - self.drawCount = drawCount + init(_winCount: Int = 0, + _loseCount: Int = 0, + _drawCount: Int = 0) { + self._winCount = _winCount + self._loseCount = _loseCount + self._drawCount = _drawCount } mutating func win() { - self.winCount += 1 + self._winCount += 1 } mutating func lose() { - self.loseCount += 1 + self._loseCount += 1 } mutating func draw() { - self.drawCount += 1 + self._drawCount += 1 } } @@ -64,15 +85,23 @@ enum Hand: CaseIterable { } protocol Playable { - + var score: GameScore { get } + var hand: Hand? { get } + + func changeHand() + func winGame() + func loseGame() + func drawGame() + func reset() } -final class User { - var score: Score - var hand: Hand + +final class User: Playable { + var score: GameScore + var hand: Hand? - init(score: Score = Score(), - hand: Hand) { + init(score: GameScore = Score(), + hand: Hand? = nil) { self.score = score self.hand = hand } @@ -88,18 +117,18 @@ final class User { } func changeHand() { - hand = hand.randomHand + hand = hand?.randomHand } func reset() { - score = .init() - changeHand() + score = Score() + hand = nil } } final class Game { - let user: User - let computer: User + let user: Playable + let computer: Playable init(user: User, computer: User) { diff --git a/RockPaperScissors/RockPaperScissorsTests/RockPaperScissorsTests.swift b/RockPaperScissors/RockPaperScissorsTests/RockPaperScissorsTests.swift index 61937dc..ba193d4 100644 --- a/RockPaperScissors/RockPaperScissorsTests/RockPaperScissorsTests.swift +++ b/RockPaperScissors/RockPaperScissorsTests/RockPaperScissorsTests.swift @@ -177,14 +177,17 @@ final class RockPaperScissorsTests: XCTestCase { sut?.game() // then - let userHand = sut?.user.hand.description - let computerHand = sut?.computer.hand.description + guard let userHand = sut?.user.hand?.description, + let computerHand = sut?.computer.hand?.description else { + return + } + let win = sut?.user.score.winCount let lose = sut?.user.score.loseCount let draw = sut?.user.score.drawCount print(">>>>>>>>>>>RANDOM TEST>>>>>>>>>>>>>>>>>>") - print("| userHand: \(userHand!)", "computerHand: \(computerHand!) |") + print("| userHand: \(userHand)", "computerHand: \(computerHand) |") print("| user Win: \(win!) \t|\n", "| user Lose: \(lose!)\t |\n", "| user Draw:\(draw!)\t |") From 86ee1de97c1b99d93c82e8c36a0b5ec6cd48e72e Mon Sep 17 00:00:00 2001 From: hsw1920 Date: Wed, 20 Mar 2024 20:31:04 +0900 Subject: [PATCH 7/8] =?UTF-8?q?Step1:=20=EB=A9=94=EC=84=9C=EB=93=9C=20?= =?UTF-8?q?=EC=A0=95=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../RockPaperScissors/Game.swift | 90 +++++----- .../GameViewController.swift | 2 - .../RockPaperScissorsTests.swift | 167 ++++++++++-------- 3 files changed, 141 insertions(+), 118 deletions(-) diff --git a/RockPaperScissors/RockPaperScissors/Game.swift b/RockPaperScissors/RockPaperScissors/Game.swift index 24459c9..d12c6c5 100644 --- a/RockPaperScissors/RockPaperScissors/Game.swift +++ b/RockPaperScissors/RockPaperScissors/Game.swift @@ -7,19 +7,11 @@ import Foundation -/// -/// 사용자와 컴퓨터의 가위 바위 보 게임입니다 -/// 사용자가 패를 선택하면 컴퓨터의 패는 임의의 패로 지정됩니다 -/// 현재 승/무/패 기록은 화면 중앙에 표시합니다 -/// 승/무/패 기록은 사용자 기준 승/무/패를 나타냅니다 -/// 삼세판 선승제로, 세 판을 먼저 이기는 쪽이 승리합니다 -/// 어느 한 쪽이 최종 승리하면 얼럿을 통해 승자를 표시하고 게임을 초기화합니다 -/// - protocol GameScore { var winCount: Int { get } var loseCount: Int { get } var drawCount: Int { get } + mutating func win() mutating func lose() mutating func draw() @@ -86,19 +78,23 @@ enum Hand: CaseIterable { protocol Playable { var score: GameScore { get } - var hand: Hand? { get } + var currentHand: Hand? { get } func changeHand() func winGame() func loseGame() func drawGame() - func reset() + func clear() } final class User: Playable { + private var hand: Hand? var score: GameScore - var hand: Hand? + + var currentHand: Hand? { + return hand + } init(score: GameScore = Score(), hand: Hand? = nil) { @@ -120,7 +116,7 @@ final class User: Playable { hand = hand?.randomHand } - func reset() { + func clear() { score = Score() hand = nil } @@ -137,49 +133,57 @@ final class Game { } func nextGame() { - user.changeHand() - computer.changeHand() - game() + setGame() + play() } - func game() { - if userWin() { + func play() { + guard let userHand = user.currentHand, + let computerHand = computer.currentHand else { + return + } + + switch judge(userHand, computerHand) { + case .userWin: user.winGame() computer.loseGame() - } - else if userLose() { - computer.winGame() + case .userLose: user.loseGame() - } else { - computer.drawGame() + computer.winGame() + case .draw: user.drawGame() + computer.drawGame() } } - - func userWin() -> Bool { - return ((user.hand == .paper && - computer.hand == .rock) || - (user.hand == .rock && - computer.hand == .scissor) || - (user.hand == .scissor && - computer.hand == .paper)) + + func judge(_ userHand: Hand, _ computerHand: Hand) -> Judge { + guard let userIndex = Hand.allCases.firstIndex(of: userHand), + let computerIndex = Hand.allCases.firstIndex(of: computerHand) else { + fatalError() + } + + let handAllCaseCount = Hand.allCases.count + + if userIndex == computerIndex { + return .draw + } else if (userIndex + 1) % handAllCaseCount == computerIndex { + return .userWin + } else { + return .userLose + } } - func userLose() -> Bool { - return ((user.hand == .paper && - computer.hand == .scissor) || - (user.hand == .rock && - computer.hand == .paper) || - (user.hand == .scissor && - computer.hand == .rock)) + func reset() { + user.clear() + computer.clear() } - func draw() -> Bool { - return !(userWin()||userLose()) + private func setGame() { + user.changeHand() + computer.changeHand() } - func resetGame() { - user.reset() - computer.reset() + enum Judge { + case userWin, userLose, draw } } diff --git a/RockPaperScissors/RockPaperScissors/GameViewController.swift b/RockPaperScissors/RockPaperScissors/GameViewController.swift index 06232db..401157a 100644 --- a/RockPaperScissors/RockPaperScissors/GameViewController.swift +++ b/RockPaperScissors/RockPaperScissors/GameViewController.swift @@ -10,8 +10,6 @@ class GameViewController: UIViewController { override func loadView() { - let randomUserHand = Hand.allCases[Int.random(in: 0..>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>") } - // MARK: nextGame 메서드 테스트 - func test_nextGame(sut: Game) { - // given - let randomUserHand = Hand.allCases[Int.random(in: 0.. sut!.computer.score.winCount { + guard let userWinCount = sut?.user.score.winCount, + let computerWinCount = sut?.computer.score.winCount else { + return + } + + if userWinCount > computerWinCount { XCTAssertEqual(sut?.user.score.winCount, 3) } else { XCTAssertEqual(sut?.computer.score.winCount, 3) } // when - sut?.resetGame() + sut?.reset() // then - XCTAssertEqual(sut?.user.score.drawCount, 0) - XCTAssertEqual(sut?.user.score.winCount, 0) - XCTAssertEqual(sut?.user.score.loseCount, 0) - XCTAssertEqual(sut?.computer.score.drawCount, 0) - XCTAssertEqual(sut?.computer.score.winCount, 0) - XCTAssertEqual(sut?.computer.score.loseCount, 0) + let user = sut?.user + let computer = sut?.computer + + let userHand = user?.currentHand + let computerHand = computer?.currentHand + + let userWin = user?.score.winCount + let userLose = user?.score.loseCount + let userDraw = user?.score.drawCount + let computerWin = computer?.score.winCount + let computerLose = computer?.score.loseCount + let computerDraw = computer?.score.drawCount + + XCTAssertNil(userHand) + XCTAssertNil(computerHand) + + XCTAssertEqual(userWin, 0) + XCTAssertEqual(userDraw, 0) + XCTAssertEqual(userLose, 0) + XCTAssertEqual(computerWin, 0) + XCTAssertEqual(computerLose, 0) + XCTAssertEqual(computerDraw, 0) + } +} + +extension RockPaperScissorsTests { + // MARK: nextGame 메서드 테스트 + private func test_nextGame(sut: Game) { + // given + let user = sut.user + let computer = sut.computer + + let userWin = user.score.winCount + let userLose = user.score.loseCount + let userDraw = user.score.drawCount + let computerWin = computer.score.winCount + let computerLose = computer.score.loseCount + let computerDraw = computer.score.drawCount + + // when + sut.nextGame() + + // then + guard let userHand = user.currentHand, + let computerHand = computer.currentHand else { + return + } + + let nextUserWin = user.score.winCount + let nextUserLose = user.score.loseCount + let nextUserDraw = user.score.drawCount + let nextComputerWin = computer.score.winCount + let nextComputerLose = computer.score.loseCount + let nextComputerDraw = computer.score.drawCount + + switch sut.judge(userHand, computerHand) { + case .userWin: + XCTAssertEqual(userWin + 1, nextUserWin) + XCTAssertEqual(computerLose + 1, nextComputerLose) + case .userLose: + XCTAssertEqual(userLose + 1, nextUserLose) + XCTAssertEqual(computerWin + 1, nextComputerWin) + case .draw: + XCTAssertEqual(userDraw + 1, nextUserDraw) + XCTAssertEqual(computerDraw + 1, nextComputerDraw) + } } } From d19ca3e371ea7d25e17caf8da396178d696d2b9e Mon Sep 17 00:00:00 2001 From: hsw1920 Date: Wed, 20 Mar 2024 21:58:18 +0900 Subject: [PATCH 8/8] =?UTF-8?q?Step1:=20=ED=8F=B4=EB=8D=94=EB=A7=81,=20Gam?= =?UTF-8?q?e=EC=9D=98=20user,=20computer=20=EC=BA=A1=EC=8A=90=ED=99=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../project.pbxproj | 72 ++++++- .../RockPaperScissors/Game.swift | 189 ------------------ .../Game/Protocol/HandGameable.swift | 15 ++ .../Game/RockPaperScissorsGame.swift | 79 ++++++++ .../RockPaperScissors/HandGameable/Hand.swift | 29 +++ .../HandGameable/Score/GameScore.swift | 52 +++++ .../HandGameable/User/Playable.swift | 53 +++++ ...swift => RockPaperScissorsGameTests.swift} | 36 ++-- 8 files changed, 308 insertions(+), 217 deletions(-) delete mode 100644 RockPaperScissors/RockPaperScissors/Game.swift create mode 100644 RockPaperScissors/RockPaperScissors/HandGameable/Game/Protocol/HandGameable.swift create mode 100644 RockPaperScissors/RockPaperScissors/HandGameable/Game/RockPaperScissorsGame.swift create mode 100644 RockPaperScissors/RockPaperScissors/HandGameable/Hand.swift create mode 100644 RockPaperScissors/RockPaperScissors/HandGameable/Score/GameScore.swift create mode 100644 RockPaperScissors/RockPaperScissors/HandGameable/User/Playable.swift rename RockPaperScissors/RockPaperScissorsTests/{RockPaperScissorsTests.swift => RockPaperScissorsGameTests.swift} (90%) diff --git a/RockPaperScissors/RockPaperScissors.xcodeproj/project.pbxproj b/RockPaperScissors/RockPaperScissors.xcodeproj/project.pbxproj index e8962fa..d8ed661 100644 --- a/RockPaperScissors/RockPaperScissors.xcodeproj/project.pbxproj +++ b/RockPaperScissors/RockPaperScissors.xcodeproj/project.pbxproj @@ -7,8 +7,12 @@ objects = { /* Begin PBXBuildFile section */ - 60CE25AA2BA962B100785863 /* RockPaperScissorsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 60CE25A92BA962B100785863 /* RockPaperScissorsTests.swift */; }; - 60CE25B12BA9652800785863 /* Game.swift in Sources */ = {isa = PBXBuildFile; fileRef = 60CE25B02BA9652800785863 /* Game.swift */; }; + 60CE25AA2BA962B100785863 /* RockPaperScissorsGameTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 60CE25A92BA962B100785863 /* RockPaperScissorsGameTests.swift */; }; + 60CE25B12BA9652800785863 /* RockPaperScissorsGame.swift in Sources */ = {isa = PBXBuildFile; fileRef = 60CE25B02BA9652800785863 /* RockPaperScissorsGame.swift */; }; + 60EC66232BAB11C6002F9AB9 /* Playable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 60EC66222BAB11C6002F9AB9 /* Playable.swift */; }; + 60EC66252BAB11F1002F9AB9 /* Hand.swift in Sources */ = {isa = PBXBuildFile; fileRef = 60EC66242BAB11F1002F9AB9 /* Hand.swift */; }; + 60EC66282BAB1208002F9AB9 /* GameScore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 60EC66272BAB1208002F9AB9 /* GameScore.swift */; }; + 60EC662A2BAB1242002F9AB9 /* HandGameable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 60EC66292BAB1242002F9AB9 /* HandGameable.swift */; }; C784841D2B5E48E300FBF8B4 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = C784841C2B5E48E300FBF8B4 /* AppDelegate.swift */; }; C784841F2B5E48E300FBF8B4 /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = C784841E2B5E48E300FBF8B4 /* SceneDelegate.swift */; }; C78484212B5E48E300FBF8B4 /* GameViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C78484202B5E48E300FBF8B4 /* GameViewController.swift */; }; @@ -29,8 +33,12 @@ /* Begin PBXFileReference section */ 60CE25A72BA962B100785863 /* RockPaperScissorsTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RockPaperScissorsTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; - 60CE25A92BA962B100785863 /* RockPaperScissorsTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RockPaperScissorsTests.swift; sourceTree = ""; }; - 60CE25B02BA9652800785863 /* Game.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Game.swift; sourceTree = ""; }; + 60CE25A92BA962B100785863 /* RockPaperScissorsGameTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RockPaperScissorsGameTests.swift; sourceTree = ""; }; + 60CE25B02BA9652800785863 /* RockPaperScissorsGame.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RockPaperScissorsGame.swift; sourceTree = ""; }; + 60EC66222BAB11C6002F9AB9 /* Playable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Playable.swift; sourceTree = ""; }; + 60EC66242BAB11F1002F9AB9 /* Hand.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Hand.swift; sourceTree = ""; }; + 60EC66272BAB1208002F9AB9 /* GameScore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GameScore.swift; sourceTree = ""; }; + 60EC66292BAB1242002F9AB9 /* HandGameable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HandGameable.swift; sourceTree = ""; }; C78484192B5E48E300FBF8B4 /* RockPaperScissors.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = RockPaperScissors.app; sourceTree = BUILT_PRODUCTS_DIR; }; C784841C2B5E48E300FBF8B4 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; C784841E2B5E48E300FBF8B4 /* SceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SceneDelegate.swift; sourceTree = ""; }; @@ -62,11 +70,55 @@ 60CE25A82BA962B100785863 /* RockPaperScissorsTests */ = { isa = PBXGroup; children = ( - 60CE25A92BA962B100785863 /* RockPaperScissorsTests.swift */, + 60CE25A92BA962B100785863 /* RockPaperScissorsGameTests.swift */, ); path = RockPaperScissorsTests; sourceTree = ""; }; + 60EC66202BAB11AA002F9AB9 /* HandGameable */ = { + isa = PBXGroup; + children = ( + 60EC662B2BAB12A0002F9AB9 /* Game */, + 60EC66262BAB11FA002F9AB9 /* Score */, + 60EC66212BAB11C0002F9AB9 /* User */, + 60EC66242BAB11F1002F9AB9 /* Hand.swift */, + ); + path = HandGameable; + sourceTree = ""; + }; + 60EC66212BAB11C0002F9AB9 /* User */ = { + isa = PBXGroup; + children = ( + 60EC66222BAB11C6002F9AB9 /* Playable.swift */, + ); + path = User; + sourceTree = ""; + }; + 60EC66262BAB11FA002F9AB9 /* Score */ = { + isa = PBXGroup; + children = ( + 60EC66272BAB1208002F9AB9 /* GameScore.swift */, + ); + path = Score; + sourceTree = ""; + }; + 60EC662B2BAB12A0002F9AB9 /* Game */ = { + isa = PBXGroup; + children = ( + 60EC662C2BAB14C9002F9AB9 /* Protocol */, + 60CE25B02BA9652800785863 /* RockPaperScissorsGame.swift */, + ); + path = Game; + sourceTree = ""; + }; + 60EC662C2BAB14C9002F9AB9 /* Protocol */ = { + isa = PBXGroup; + children = ( + 60EC66292BAB1242002F9AB9 /* HandGameable.swift */, + ); + path = Protocol; + sourceTree = ""; + }; C78484102B5E48E300FBF8B4 = { isa = PBXGroup; children = ( @@ -92,7 +144,7 @@ C784841E2B5E48E300FBF8B4 /* SceneDelegate.swift */, C78484202B5E48E300FBF8B4 /* GameViewController.swift */, C78484632B6A32E500FBF8B4 /* GameView.swift */, - 60CE25B02BA9652800785863 /* Game.swift */, + 60EC66202BAB11AA002F9AB9 /* HandGameable */, C78484252B5E48E400FBF8B4 /* Assets.xcassets */, C78484272B5E48E400FBF8B4 /* LaunchScreen.storyboard */, C784842A2B5E48E400FBF8B4 /* Info.plist */, @@ -200,7 +252,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - 60CE25AA2BA962B100785863 /* RockPaperScissorsTests.swift in Sources */, + 60CE25AA2BA962B100785863 /* RockPaperScissorsGameTests.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -209,8 +261,12 @@ buildActionMask = 2147483647; files = ( C78484212B5E48E300FBF8B4 /* GameViewController.swift in Sources */, - 60CE25B12BA9652800785863 /* Game.swift in Sources */, + 60CE25B12BA9652800785863 /* RockPaperScissorsGame.swift in Sources */, + 60EC66282BAB1208002F9AB9 /* GameScore.swift in Sources */, + 60EC66232BAB11C6002F9AB9 /* Playable.swift in Sources */, C784841D2B5E48E300FBF8B4 /* AppDelegate.swift in Sources */, + 60EC662A2BAB1242002F9AB9 /* HandGameable.swift in Sources */, + 60EC66252BAB11F1002F9AB9 /* Hand.swift in Sources */, C78484642B6A32E500FBF8B4 /* GameView.swift in Sources */, C784841F2B5E48E300FBF8B4 /* SceneDelegate.swift in Sources */, ); diff --git a/RockPaperScissors/RockPaperScissors/Game.swift b/RockPaperScissors/RockPaperScissors/Game.swift deleted file mode 100644 index d12c6c5..0000000 --- a/RockPaperScissors/RockPaperScissors/Game.swift +++ /dev/null @@ -1,189 +0,0 @@ -// -// Game.swift -// RockPaperScissors -// -// Created by 홍승완 on 2024/03/19. -// - -import Foundation - -protocol GameScore { - var winCount: Int { get } - var loseCount: Int { get } - var drawCount: Int { get } - - mutating func win() - mutating func lose() - mutating func draw() -} - -struct Score: GameScore { - private var _winCount: Int - private var _loseCount: Int - private var _drawCount: Int - - var winCount: Int { - return _winCount - } - - var loseCount: Int { - return _loseCount - } - - var drawCount: Int { - return _drawCount - } - - init(_winCount: Int = 0, - _loseCount: Int = 0, - _drawCount: Int = 0) { - self._winCount = _winCount - self._loseCount = _loseCount - self._drawCount = _drawCount - } - - mutating func win() { - self._winCount += 1 - } - mutating func lose() { - self._loseCount += 1 - } - mutating func draw() { - self._drawCount += 1 - } -} - -enum Hand: CaseIterable { - case paper, rock, scissor - - var description: String { - switch self { - case .paper: return "🖐️" - case .rock: return "✊" - case .scissor: return "✌️" - } - } - - var randomHand: Hand { - return random() - } - - private func random() -> Hand { - guard let randomUserHand = Hand.allCases.randomElement() else { - fatalError(">>> Error: random() in Hand") - } - return randomUserHand - } -} - -protocol Playable { - var score: GameScore { get } - var currentHand: Hand? { get } - - func changeHand() - func winGame() - func loseGame() - func drawGame() - func clear() -} - - -final class User: Playable { - private var hand: Hand? - var score: GameScore - - var currentHand: Hand? { - return hand - } - - init(score: GameScore = Score(), - hand: Hand? = nil) { - self.score = score - self.hand = hand - } - - func winGame() { - score.win() - } - func loseGame() { - score.lose() - } - func drawGame() { - score.draw() - } - - func changeHand() { - hand = hand?.randomHand - } - - func clear() { - score = Score() - hand = nil - } -} - -final class Game { - let user: Playable - let computer: Playable - - init(user: User, - computer: User) { - self.user = user - self.computer = computer - } - - func nextGame() { - setGame() - play() - } - - func play() { - guard let userHand = user.currentHand, - let computerHand = computer.currentHand else { - return - } - - switch judge(userHand, computerHand) { - case .userWin: - user.winGame() - computer.loseGame() - case .userLose: - user.loseGame() - computer.winGame() - case .draw: - user.drawGame() - computer.drawGame() - } - } - - func judge(_ userHand: Hand, _ computerHand: Hand) -> Judge { - guard let userIndex = Hand.allCases.firstIndex(of: userHand), - let computerIndex = Hand.allCases.firstIndex(of: computerHand) else { - fatalError() - } - - let handAllCaseCount = Hand.allCases.count - - if userIndex == computerIndex { - return .draw - } else if (userIndex + 1) % handAllCaseCount == computerIndex { - return .userWin - } else { - return .userLose - } - } - - func reset() { - user.clear() - computer.clear() - } - - private func setGame() { - user.changeHand() - computer.changeHand() - } - - enum Judge { - case userWin, userLose, draw - } -} diff --git a/RockPaperScissors/RockPaperScissors/HandGameable/Game/Protocol/HandGameable.swift b/RockPaperScissors/RockPaperScissors/HandGameable/Game/Protocol/HandGameable.swift new file mode 100644 index 0000000..b8b2960 --- /dev/null +++ b/RockPaperScissors/RockPaperScissors/HandGameable/Game/Protocol/HandGameable.swift @@ -0,0 +1,15 @@ +// +// HandGameable.swift +// RockPaperScissors +// +// Created by 홍승완 on 2024/03/20. +// + +protocol HandGameable { + var user: Playable { get } + var computer: Playable { get } + + func play() + func reset() + func nextGame() +} diff --git a/RockPaperScissors/RockPaperScissors/HandGameable/Game/RockPaperScissorsGame.swift b/RockPaperScissors/RockPaperScissors/HandGameable/Game/RockPaperScissorsGame.swift new file mode 100644 index 0000000..842821d --- /dev/null +++ b/RockPaperScissors/RockPaperScissors/HandGameable/Game/RockPaperScissorsGame.swift @@ -0,0 +1,79 @@ +// +// Game.swift +// RockPaperScissors +// +// Created by 홍승완 on 2024/03/19. +// + +import Foundation + +final class RockPaperScissorsGame: HandGameable { + private let _user: Playable + private let _computer: Playable + + var user: Playable { + return _user + } + + var computer: Playable { + return _computer + } + + init(user: User, + computer: User) { + self._user = user + self._computer = computer + } + + func nextGame() { + setGame() + play() + } + + func play() { + switch judge() { + case .userWin: + user.winGame() + computer.loseGame() + case .userLose: + user.loseGame() + computer.winGame() + case .draw: + user.drawGame() + computer.drawGame() + } + } + + func judge() -> Judge { + guard let userHand = user.currentHand, + let computerHand = computer.currentHand, + let userIndex = Hand.allCases.firstIndex(of: userHand), + let computerIndex = Hand.allCases.firstIndex(of: computerHand) else { + fatalError() + } + + let handAllCaseCount = Hand.allCases.count + + if userIndex == computerIndex { + return .draw + } else if (userIndex + 1) % handAllCaseCount == computerIndex { + return .userWin + } else { + return .userLose + } + } + + func reset() { + user.clear() + computer.clear() + } + + private func setGame() { + user.changeHand() + computer.changeHand() + } + + enum Judge { + case userWin, userLose, draw + } +} diff --git a/RockPaperScissors/RockPaperScissors/HandGameable/Hand.swift b/RockPaperScissors/RockPaperScissors/HandGameable/Hand.swift new file mode 100644 index 0000000..48aaa97 --- /dev/null +++ b/RockPaperScissors/RockPaperScissors/HandGameable/Hand.swift @@ -0,0 +1,29 @@ +// +// Hand.swift +// RockPaperScissors +// +// Created by 홍승완 on 2024/03/20. +// + +enum Hand: CaseIterable { + case paper, rock, scissor + + var description: String { + switch self { + case .paper: return "🖐️" + case .rock: return "✊" + case .scissor: return "✌️" + } + } + + var randomHand: Hand { + return random() + } + + private func random() -> Hand { + guard let randomUserHand = Hand.allCases.randomElement() else { + fatalError(">>> Error: random() in Hand") + } + return randomUserHand + } +} diff --git a/RockPaperScissors/RockPaperScissors/HandGameable/Score/GameScore.swift b/RockPaperScissors/RockPaperScissors/HandGameable/Score/GameScore.swift new file mode 100644 index 0000000..7288cee --- /dev/null +++ b/RockPaperScissors/RockPaperScissors/HandGameable/Score/GameScore.swift @@ -0,0 +1,52 @@ +// +// Score.swift +// RockPaperScissors +// +// Created by 홍승완 on 2024/03/20. +// + +protocol GameScore { + var winCount: Int { get } + var loseCount: Int { get } + var drawCount: Int { get } + + mutating func win() + mutating func lose() + mutating func draw() +} + +struct Score: GameScore { + private var _winCount: Int + private var _loseCount: Int + private var _drawCount: Int + + var winCount: Int { + return _winCount + } + + var loseCount: Int { + return _loseCount + } + + var drawCount: Int { + return _drawCount + } + + init(winCount: Int = 0, + loseCount: Int = 0, + drawCount: Int = 0) { + self._winCount = winCount + self._loseCount = loseCount + self._drawCount = drawCount + } + + mutating func win() { + self._winCount += 1 + } + mutating func lose() { + self._loseCount += 1 + } + mutating func draw() { + self._drawCount += 1 + } +} diff --git a/RockPaperScissors/RockPaperScissors/HandGameable/User/Playable.swift b/RockPaperScissors/RockPaperScissors/HandGameable/User/Playable.swift new file mode 100644 index 0000000..efd73e0 --- /dev/null +++ b/RockPaperScissors/RockPaperScissors/HandGameable/User/Playable.swift @@ -0,0 +1,53 @@ +// +// User.swift +// RockPaperScissors +// +// Created by 홍승완 on 2024/03/20. +// + +protocol Playable { + var score: GameScore { get } + var currentHand: Hand? { get } + + func changeHand() + func winGame() + func loseGame() + func drawGame() + func clear() +} + + +final class User: Playable { + private var hand: Hand? + var score: GameScore + + var currentHand: Hand? { + return hand + } + + init(score: GameScore = Score(), + hand: Hand? = nil) { + self.score = score + self.hand = hand + } + + func winGame() { + score.win() + } + func loseGame() { + score.lose() + } + func drawGame() { + score.draw() + } + + func changeHand() { + hand = hand?.randomHand + } + + func clear() { + score = Score() + hand = nil + } +} + diff --git a/RockPaperScissors/RockPaperScissorsTests/RockPaperScissorsTests.swift b/RockPaperScissors/RockPaperScissorsTests/RockPaperScissorsGameTests.swift similarity index 90% rename from RockPaperScissors/RockPaperScissorsTests/RockPaperScissorsTests.swift rename to RockPaperScissors/RockPaperScissorsTests/RockPaperScissorsGameTests.swift index 48f24b6..a65ebf9 100644 --- a/RockPaperScissors/RockPaperScissorsTests/RockPaperScissorsTests.swift +++ b/RockPaperScissors/RockPaperScissorsTests/RockPaperScissorsGameTests.swift @@ -8,13 +8,13 @@ import XCTest @testable import RockPaperScissors -final class RockPaperScissorsTests: XCTestCase { - var sut: Game? +final class RockPaperScissorsGameTests: XCTestCase { + var sut: RockPaperScissorsGame? // MARK: 보로 비겼을 때 func test_paper_draw() throws { // given - sut = Game(user: User(hand: .paper), + sut = RockPaperScissorsGame(user: User(hand: .paper), computer: User(hand: .paper)) // when @@ -30,7 +30,7 @@ final class RockPaperScissorsTests: XCTestCase { // MARK: 가위로 비겼을 때 func test_scissor_draw() throws { // given - sut = Game(user: User(hand: .scissor), + sut = RockPaperScissorsGame(user: User(hand: .scissor), computer: User(hand: .scissor)) // when @@ -46,7 +46,7 @@ final class RockPaperScissorsTests: XCTestCase { // MARK: 주먹으로 비겼을 때 func test_rock_draw() throws { // given - sut = Game(user: User(hand: .rock), + sut = RockPaperScissorsGame(user: User(hand: .rock), computer: User(hand: .rock)) // when @@ -62,7 +62,7 @@ final class RockPaperScissorsTests: XCTestCase { // MARK: 유저가 보를 내서 이길 때 func test_paper_user_win() throws { // given - sut = Game(user: User(hand: .paper), + sut = RockPaperScissorsGame(user: User(hand: .paper), computer: User(hand: .rock)) // when @@ -78,7 +78,7 @@ final class RockPaperScissorsTests: XCTestCase { // MARK: 유저가 가위를 내서 이길 때 func test_scissor_user_win() throws { // given - sut = Game(user: User(hand: .scissor), + sut = RockPaperScissorsGame(user: User(hand: .scissor), computer: User(hand: .paper)) // when @@ -94,7 +94,7 @@ final class RockPaperScissorsTests: XCTestCase { // MARK: 유저가 바위를 내서 이길 때 func test_rock_user_win() throws { // given - sut = Game(user: User(hand: .rock), + sut = RockPaperScissorsGame(user: User(hand: .rock), computer: User(hand: .scissor)) // when @@ -110,7 +110,7 @@ final class RockPaperScissorsTests: XCTestCase { // MARK: 컴퓨터가 보를 내서 이길 때 func test_paper_computer_win() throws { // given - sut = Game(user: User(hand: .rock), + sut = RockPaperScissorsGame(user: User(hand: .rock), computer: User(hand: .paper)) // when @@ -126,7 +126,7 @@ final class RockPaperScissorsTests: XCTestCase { // MARK: 컴퓨터가 가위를 내서 이길 때 func test_scissor_computer_win() throws { // given - sut = Game(user: User(hand: .paper), + sut = RockPaperScissorsGame(user: User(hand: .paper), computer: User(hand: .scissor)) // when @@ -142,7 +142,7 @@ final class RockPaperScissorsTests: XCTestCase { // MARK: 컴퓨터가 바위를 내서 이길 때 func test_rock_computer_win() throws { // given - sut = Game(user: User(hand: .scissor), + sut = RockPaperScissorsGame(user: User(hand: .scissor), computer: User(hand: .rock)) // when @@ -160,7 +160,7 @@ final class RockPaperScissorsTests: XCTestCase { // given let randomUserHand = Hand.allCases[Int.random(in: 0..