diff --git a/TODO.md b/TODO.md index 2dc257d..0ed01fb 100644 --- a/TODO.md +++ b/TODO.md @@ -1 +1,19 @@ # 턴제 게임 + +객체: +character(name, hp, mp) +action +turn(turn) + +- character 기능 목록 + 1. action + 2. lose hp + 3. lose mp +- action 기능 목록 + 1. attack + 2. defense + 3. attack twice + 4. attack three times + 5. hit strongly +- turn 기능 목록 + 1. inTurn \ No newline at end of file diff --git a/build.gradle b/build.gradle index b540f64..7f41072 100644 --- a/build.gradle +++ b/build.gradle @@ -1,5 +1,7 @@ plugins { id 'java' + id 'org.springframework.boot' version '3.1.6' + id 'io.spring.dependency-management' version '1.1.3' } group = 'com.gdsc' @@ -18,6 +20,15 @@ repositories { dependencies { testImplementation 'org.junit.jupiter:junit-jupiter:5.7.1' testRuntimeOnly 'org.junit.platform:junit-platform-launcher' + implementation 'org.springframework.boot:spring-boot-starter' // 기본 스프링 부트 의존성 + implementation 'org.springframework.boot:spring-boot-starter-web' // REST API 개발용 + testImplementation 'org.springframework.boot:spring-boot-starter-test' // 테스트용 + implementation 'com.fasterxml.jackson.core:jackson-databind' + implementation("org.springframework.boot:spring-boot-starter-data-jpa") //jpa 의존성 + implementation group: 'org.postgresql', name: 'postgresql', version: '42.5.0' //postgresql + compileOnly 'org.projectlombok:lombok:1.18.36' //lombok + annotationProcessor 'org.projectlombok:lombok:1.18.36' + runtimeOnly 'org.postgresql:postgresql' } tasks.named('test') { diff --git a/src/main/java/com/gdsc/game/Action.java b/src/main/java/com/gdsc/game/Action.java new file mode 100644 index 0000000..fefbb1c --- /dev/null +++ b/src/main/java/com/gdsc/game/Action.java @@ -0,0 +1,62 @@ +package com.gdsc.game; + +interface interfaceAction{ + int attack(); + int defense(); + int attack2(Character A); + int attack3(Character A); + int attackHard(Character A); +} + +public class Action implements interfaceAction { + + public void coolDown(Character A,Character B){ //skill5 한정 + if(A.getSkill5Cool()>0){ + A.setSkill5Cool(A.getSkill5Cool()-1); + } + if(B.getSkill5Cool()>0){ + B.setSkill5Cool(B.getSkill5Cool()-1); + } + + } + + @Override + public int attack(){ + int damage = (int)(Math.random() * 10) + 1; + System.out.println("#:"+ damage); //test용 + return damage; + } + + @Override + public int defense(){ + int shield = (int)(Math.random() * 10) + 1; + System.out.println("#:"+ shield); //test용 + return shield; + + } + + @Override + public int attack2(Character A){ + int damage = 2*((int)(Math.random() * 10) + 1); + System.out.println("#:"+ damage); //test용 + return damage; + + } + + @Override + public int attack3(Character A){ + int damage = 3*((int)(Math.random() * 10) + 1); + System.out.println("#:"+ damage); //test용 + return damage; + + } + + @Override + public int attackHard(Character A){ + int damage = 5*((int)(Math.random() * 10) + 1); + System.out.println("#:"+ damage); //test용 + return damage; + + } +} + diff --git a/src/main/java/com/gdsc/game/Application.java b/src/main/java/com/gdsc/game/Application.java index 0eef710..cf2abbf 100644 --- a/src/main/java/com/gdsc/game/Application.java +++ b/src/main/java/com/gdsc/game/Application.java @@ -1,9 +1,43 @@ package com.gdsc.game; -public class Application { + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +import java.util.Scanner; + + +@SpringBootApplication +public class Application{ public static void main(String[] args) { - System.out.println("hello world"); +// System.out.println("write two character"); + +// Scanner sc = new Scanner(System.in); +// String input = sc.nextLine(); + +// String[] names = input.split(","); +// String name1 = names[0]; +// String name2 = names[1]; + + SpringApplication.run(Application.class, args); + +// String name1 = "knight"; +// String name2 = "slime"; +// +// Character A = new Character(name1); +// Character B = new Character(name2); +// +//// System.out.println("How many turns?"); +//// int turn = sc.nextInt(); +// +// int turn = 7; +// + Game runGame = new Game(); + runGame.runGame(); + +// sc.close(); + } } diff --git a/src/main/java/com/gdsc/game/Character.java b/src/main/java/com/gdsc/game/Character.java new file mode 100644 index 0000000..a909a2c --- /dev/null +++ b/src/main/java/com/gdsc/game/Character.java @@ -0,0 +1,114 @@ +package com.gdsc.game; + +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; + +interface interfaceCharacter{ + + void setName(String name); + void losehp(int digit); + void losemp(int digit); + boolean alive(); +} +@Entity +public class Character implements interfaceCharacter{ + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + private String name; + private int hp; + + public void setHp(int hp) { + this.hp = hp; + } + + public void setMp(int mp) { + this.mp = mp; + } + + private int mp; + + public String getJob() { + return job; + } + + public void setJob(String job) { + this.job = job; + } + + private String job; + + + public int getLevel() { + return level; + } + + public void setLevel(int level) { + this.level = level; + } + + private int level; + + private int shield; + + private int skill5Cool; + + public int getSkill5Cool() { + return skill5Cool; + } + + public void setSkill5Cool(int skillCool) { + this.skill5Cool = skillCool; + } + + + + public int getShield() { + return shield; + } + + public void setShield(int shield) { + this.shield = shield; + } + + public Character(){ + } + + public int getHp() { + return hp; + } + + public int getMp() { + return mp; + } + + public String getName() { + return name; + } + + @Override + public void setName(String name){ + this.name = name; + } + + @Override + public void losehp(int digit){ + this.hp -= digit; + } + + @Override + public void losemp(int digit) { + this.mp -= digit; + } + + @Override + public boolean alive(){ + if(this.hp>0) return true; + else return false; + } + +} diff --git a/src/main/java/com/gdsc/game/CharacterDAO.java b/src/main/java/com/gdsc/game/CharacterDAO.java new file mode 100644 index 0000000..d2d0dc7 --- /dev/null +++ b/src/main/java/com/gdsc/game/CharacterDAO.java @@ -0,0 +1,17 @@ +package com.gdsc.game; + +import jakarta.persistence.EntityManager; +import jakarta.persistence.PersistenceContext; +import jakarta.transaction.Transactional; +import org.springframework.stereotype.Repository; + +@Repository +public class CharacterDAO { + @PersistenceContext + private EntityManager entityManager; + + @Transactional + public void save(Character character) { + entityManager.persist(character); // 엔티티를 데이터베이스에 저장 + } +} diff --git a/src/main/java/com/gdsc/game/CharacterDTO.java b/src/main/java/com/gdsc/game/CharacterDTO.java new file mode 100644 index 0000000..b91bb23 --- /dev/null +++ b/src/main/java/com/gdsc/game/CharacterDTO.java @@ -0,0 +1,20 @@ +package com.gdsc.game; + +public class CharacterDTO { + private String name; + private String job; + private Integer level; + + public String getName() { + return name; + } + + public String getJob() { + return job; + } + + public Integer getLevel() { + return level; + } + +} diff --git a/src/main/java/com/gdsc/game/CharacterService.java b/src/main/java/com/gdsc/game/CharacterService.java new file mode 100644 index 0000000..632417e --- /dev/null +++ b/src/main/java/com/gdsc/game/CharacterService.java @@ -0,0 +1,39 @@ +package com.gdsc.game; + +import org.springframework.stereotype.Service; + +@Service +public class CharacterService { + private final CharacterDAO characterDAO; + + public CharacterService(CharacterDAO characterDAO) { + this.characterDAO = characterDAO; + } + + public int getHpByJob(Job job) { + return job.getHp(); + } + + public int getMpByJob(Job job) { + return job.getMp(); + } + + public Character registerCharacter(String name, String job, int level) { + Character character = new Character(); + character.setName(name); + character.setJob(job); + character.setLevel(level); + + Job jobEnum = Job.valueOf(job.toUpperCase()); + character.setJob(jobEnum.toString()); + + //기본 hp와 mp + character.setHp(getHpByJob(jobEnum)+level*10); + character.setMp(getMpByJob(jobEnum)+level*5); + + //dao가 저장하게끔 함 + characterDAO.save(character); + + return character; + } +} diff --git a/src/main/java/com/gdsc/game/Game.java b/src/main/java/com/gdsc/game/Game.java new file mode 100644 index 0000000..7362d4e --- /dev/null +++ b/src/main/java/com/gdsc/game/Game.java @@ -0,0 +1,143 @@ +package com.gdsc.game; + +import org.springframework.stereotype.Service; + +import java.util.*; +import java.util.function.Supplier; + +import static java.lang.Math.max; + +@Service +public class Game { + + private Map characters = new HashMap<>(); //character list + private int turn; //현재 turn + private int end; // 총 turn 횟수 + + private Character A; + private Character B; + + Action action = new Action(); + + + + public Game() { //매개변수 turn은 총 횟수 + characters.put("knight",new Character()); + characters.put("slime",new Character()); + this.end = 7; + } + + public Character getCharacterState(String name){ + return characters.get(name); + } + + public String getAvailableSKill(String name){ + Character character = characters.get(name); + return name+"'s Available skill(cooltime) : attack2(0), attack3(0), attackHard("+Integer.toString(character.getSkill5Cool())+")"; + } + + public String performAction(String name, int number){ + int amount; + if(name.equals("knight")){ + Character A = characters.get("knight"); + Character B = characters.get("slime"); + amount = act(A,B,number); + } + else{ //slime + Character A = characters.get("slime"); + Character B = characters.get("knight"); + amount = act(A,B,number); + } + return name+"'s damage or shield:"+amount; + } + + public int act(Character A,Character B, int choice){ //행위자는 A (A가 공격 or A shield 생성) + A.setShield(0); //shield를 초기화 (이미 썼거나 처음인 경우) + int[] actionList = {action.attack(), action.defense(), action.attack2(A), action.attack3(A), action.attackHard(A)}; //인덱스 0~4 + if (choice ==2){ + int shield = actionList[choice-1]; + A.setShield(shield); + return shield; + } else { //choice == 1 or 3 or 4 or 5 + if(A.getMp()0){ //쿨타임이 존재하는 스킬일 경우(인덱스=4) + System.out.println("skill is cool"); + return 0; + } + int opponentShield = B.getShield(); + if(choice==5){ + A.setSkill5Cool(3); //턴 끝날때 --을 해주기에 결과적으로 2번의 쿨타임을 가짐 + } + A.losemp(mana(choice)); + int damage = actionList[choice-1]; + B.losehp(max(0,damage- opponentShield)); //음수를 데미지 입힐 수 없음 + return damage; + } + + } + + + public void runGame() { + this.A = characters.get("knight"); + this.B = characters.get("slime"); + + for (turn = 0; A.alive() && B.alive() && turn < end; turn++) { + System.out.println("turn:"+ turn); +// Scanner sc = new Scanner(System.in); + //A먼저 진행 + print(A,B); +// int choiceA = sc.nextInt(); +// act(A,B,choiceA); + + if(!B.alive()){ //A공격으로 B 사망 + break; + } + + print(B,A); +// int choiceB = sc.nextInt(); +// act(B,A,choiceB); + action.coolDown(A,B); + } + + //for문 끝났을 경우 -> turn 횟수 소진 & 한 명 죽음 + if (A.getHp() < B.getHp()) { + System.out.println(B.getName() + " win"); + } else if (A.getHp() > B.getHp()) { + System.out.println(A.getName() + " win"); + } else { + System.out.println("draw"); + } + + + } + + //공격 마나 + public int mana(int choice){ + if(choice==1){ + return 0; + } + else if(choice==3){ + return 2; + } + else if(choice==4){ + return 3; + } + else{ //choice==5 + return 5; + } + } + + public void print(Character A, Character B) { + System.out.println(A.getName() + " hp:" + A.getHp() + " mp:" + A.getMp() + " | " + B.getName() + " hp:" + B.getHp() + " mp:" + B.getMp()); + System.out.println("1. attack(1~10)"); + System.out.println("2. defense(1~10)"); + System.out.println("3. double attack(2~20) - 2MP - 0turn"); + System.out.println("4. triple attack(3~30) - 3MP - 0turn"); + System.out.println("5. hard attack(5~50) - 5MP - 2turns"); + } + +} + diff --git a/src/main/java/com/gdsc/game/GameController.java b/src/main/java/com/gdsc/game/GameController.java new file mode 100644 index 0000000..2547b6d --- /dev/null +++ b/src/main/java/com/gdsc/game/GameController.java @@ -0,0 +1,78 @@ +package com.gdsc.game; + +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +@RestController +@RequestMapping("/game") +public class GameController { + + private final Game gameService; + private final CharacterService characterService; + + public GameController(Game gameService, CharacterService characterService) { + this.gameService = gameService; + this.characterService = characterService; + } + + @GetMapping("/character/{name}/state") + public Character getCharacterState(@PathVariable String name) { + return gameService.getCharacterState(name); + } + + @GetMapping("/character/{name}/skill") + public String getAvailableSkill(@PathVariable String name){ + return gameService.getAvailableSKill(name); + } + + + + @PostMapping("/action") + public String performAction(@RequestBody ActionRequest request) { + System.out.println(request.getName()); + System.out.println(request.getNumber()); + return gameService.performAction(request.getName(), request.getNumber()); + } + public static class ActionRequest { + private String name; + private int number; + + public String getName() { + return name; + } + + public int getNumber() { + return number; + } + + } + + @PostMapping("/register") + public ResponseEntity registerCharacter(@RequestBody CharacterDTO characterRequest){ + + String name = characterRequest.getName(); + String job = characterRequest.getJob(); + Integer level = characterRequest.getLevel(); + + //지정되지 않은 형식 + if (name == null || name.isBlank() || job == null || level==null|| level <= 0) { + return ResponseEntity.badRequest().build(); + } + + //존재하지 않는 직업 + Job existingJob; + try { + existingJob = Job.valueOf(job.toUpperCase()); + } catch (IllegalArgumentException e) { + return ResponseEntity.status(HttpStatus.NOT_FOUND).build(); + } + + // 등록 + Character character = characterService.registerCharacter(name, job, level); + String message = "registered"; + return ResponseEntity.ok(message); + + } + +} diff --git a/src/main/java/com/gdsc/game/Job.java b/src/main/java/com/gdsc/game/Job.java new file mode 100644 index 0000000..5a05b19 --- /dev/null +++ b/src/main/java/com/gdsc/game/Job.java @@ -0,0 +1,37 @@ +package com.gdsc.game; + +import java.util.Arrays; +import java.util.List; + +public enum Job { + //체력,마나,스킬 + KNIGHT("Knight",100,0,Arrays.asList("attack,defense")), + WIZARD("Wizard",50,50,Arrays.asList("defense,attack2,attack3,attackHard")); + + public String getJob() { + return job; + } + + private String job; + + public int getHp() { + return hp; + } + + public int getMp() { + return mp; + } + + private int hp; + private int mp; + private List skills; + + Job(String job, int hp, int mp, List skills) { + this.hp = hp; + this.mp = mp; + this.skills = skills; + } + + + +} diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index e5f34ee..de8f037 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -1 +1,20 @@ spring.application.name=2024-2-mission-course-java + +# 연결 +spring.datasource.url=jdbc:postgresql://localhost:5432/gdsc_spring +spring.datasource.username=postgres +spring.datasource.password=1234 + +## SQL DB +#spring.jpa.properties.hibernate.format_sql=true +#spring.jpa.properties.hibernate.show_sql=true +#spring.jpa.properties.logging.level.org.hibernate.SQL=debug +#spring.jpa.show-sql=true + +# JPA +spring.jpa.hibernate.ddl-auto=update +#spring.jpa.properties.hibernate.format_sql = true +spring.jpa.show-sql = true + +# Postgres +spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.PostgreSQLDialect \ No newline at end of file