숫자야구 게임을 코드로 구현해 보았다.
팀원과 함께 머리를 맞대고 짜보았다.
규칙은 아래 링크를 참고하자
https://namu.wiki/w/%EC%88%AB%EC%9E%90%EC%95%BC%EA%B5%AC
숫자야구 - 나무위키
간단하게 내기삼아 할 수 있는 게임이며 연필 및 종이 게임이다. 원제는 Bulls and Cows 이다. Bulls and Cows는 상업적으로 판매되는 보드 게임 마스터마인드보다 앞서 두 명 이상의 플레이어를 위한 오
namu.wiki
다음은 메인 코드와 경우의 수 파악과 맞춰야 할 수를 생성하는 메서드이다
import java.util.*;
public class Baseball {
TreeSet<String> guess=new TreeSet<String>();
Set<Integer> overlap=new HashSet<Integer>();
Integer[] myArr=new Integer[] {0,1,2,3,4,5,6,7,8,9};
int index=0;
int strike=0;
int ball=0;
int cnt=0; // 몇번만에 답을 확인했는지를 위해
int flag=0;
String[] batter=new String[3];
String number=null;
boolean repeat=true;
public static void main(String[] args) {
Baseball b=new Baseball();
for(int i=0;i<1000;i++) {
b.repeat=true;
b.randMaker(); // 3스트라이크 잡아야 할 수
b.perm(b.myArr, 0); //가능성 있는수 뽑기
b.startGame(); //경기 시작
b.overlap.clear();//한게임 끝나면 경우의 수
}
System.out.println("1000게임 후 평균: "+b.cnt/1000);
}
public void perm(Integer[] arr,int depth) {//경우의 수를 뽑기 위해 10P3 경우
if(depth==3) {//숫자 10개중 3개뽑았을때 조합가능한 수를 set에 추가
guess.add(printNum(arr));//경우의 수들 set 리스트에 추가
return;
}
for(int i=depth;i<10;i++) {//10개숫자내에서 조합가능한수 스왑
swap(arr,depth,i);
perm(arr,depth+1);
swap(arr,depth,i);
}
}
public void swap(Integer[] arr,int depth,int i) {
int temp=arr[depth];
arr[depth]=arr[i];
arr[i]=temp;
}
public String printNum(Integer[] num) { //숫자를 문자열로 바꾸기 위해
String changer=null;
int pNum=num[0]*100+num[1]*10+num[2]*1;
if(pNum<100) {
changer="0"+Integer.toString(pNum);
}else {
changer=Integer.toString(pNum);
}
return changer;
}
public void randMaker() { //맞출 수를 랜덤 생성
Random rand=new Random();
for(int i=0; i<batter.length; i++) {
int randNum = rand.nextInt(10);
if(overlap.contains(randNum)) {//중복확인
i--;
continue;
}else {
batter[i] = randNum+"";//문자열로 추가
overlap.add(randNum);
}
}
}
다음은 경기장? 판을 깔아주기 위한 메서드이다.
public void startGame() {
while(repeat) {
Iterator<String> iterator = guess.iterator();
while(iterator.hasNext()) {
if(repeat==false) {
break;
}
Iterator<String> clone = guess.iterator();
int size=0;//경우의수의 갯수를 넣기 위해
while(clone.hasNext()) { //경우의수가 몇개 있는지 파악하기 위해서
clone.next();
size++;//
}
clone=guess.iterator();
int ranThrow=new Random().nextInt(size);//경우의갯수를 크기로 난수던질준비
int cloneBall=0;
while(clone.hasNext()) {
if(cloneBall==ranThrow) { //난수번째 때 던지는거지 공을
number=clone.next();//던질수가 설정됨
break;
}
clone.next(); //같지 않으니 넘겨
cloneBall++;
}
// while(clone.hasNext()) { // 원래는 난수로 던지지않고 경우의 수 가운데 값만 게속 던지게 했엇음
// if(cloneBall==(size/2)) {
// number=clone.next();
// break;
// }
// clone.next();
// cloneBall++;
// }
guess.remove(number); // 현재 비교할 값은 제거 이유: 어차피 다시는 이숫자를 안쓸꺼기 때문
iterator=guess.iterator();//위에 수 제거했으니깐 다시 iterator 생성
System.out.println(number+" 던진다 ~"); //던질수 출력
ballCount(number); //스트라이크 볼 갯수 알기 위한 메소드
refree(iterator);// 심판같은역할 필요없는 경우의 수들을 제거를 위한 메소드
}
}
}
public void ballCount(String number) { //스트라이크 볼 전광판같은느낌
for(int i=0;i<batter.length;i++) {
if(number.contains(batter[i])) { // 던진수(난수)가 맞춰야 할 수의 숫자들을 가지고 있다면
int index=number.indexOf(batter[i]);
if(index==i) {//해당번호와 자리수가 같으면
strike++;// 스트라이크 카운트 증가
}else {
ball++;//볼 카운트 증가
}
}
}
System.out.print("스트라이크 존: ");
for(int i=0; i<batter.length; i++) {
System.out.print(batter[i]);// 맞춰야 할 수 출력
}
System.out.println();
System.out.println(strike+" Strike~~");// 몇 스트라이크
System.out.println(ball+" Ball~~"); // 몇 볼
cnt++;
}
public void refree(Iterator<String> iterator) {
if(strike == 0 && ball == 0) { //0strike 0ball
out(number,iterator);
}else if(strike == 0 && ball ==1) {//0strike 1ball
oneB(number,iterator);
}else if(strike == 0 && ball == 2) {//0strike 2ball
twoB(number, iterator);
}else if(strike == 0 && ball == 3) {//0strike 3ball
threeB(number, iterator);
}else if(strike ==1 && ball == 0){//1strike 0ball
oneS(number,iterator);
}else if(strike == 1 && ball == 1) {//1strike 1ball
oneSOneB(number, iterator);
}else if(strike == 1 && ball ==2) {//1strike 2ball
oneSTwoB(number,iterator);
}else if(strike == 2 && ball == 0){//2strike
twoS(number,iterator);
}else {//삼진
threeS(number,iterator);
strike=0; //삼진 초기화
ball=0; //삼진 초기화
repeat=false;//게임 종료
//System.exit(0);
}
strike=0;
ball=0;
}
1 ball 일 때 메커니즘 자기가 현재 서있는 자리는 자기가 있을 수 있는 수가 절대 아니다.
ex) 123이 1 ball일 때
1xx 형태 인 수 전부 삭제
x2x 형태인 수 전부 삭제
xx3 형태인 수 전부 삭제
public void oneB(String number,Iterator<String> iterator) {// 1ball이란 말은 자기들중에 있는수가 있는데 자기 자신의 자리는 절때 아니라는거니깐
while (iterator.hasNext()) {
String nextNum = iterator.next();
if(number.charAt(0) == nextNum.charAt(0)) {
iterator.remove();
}else if(number.charAt(1) == nextNum.charAt(1)) {
iterator.remove();
}else if(number.charAt(2) == nextNum.charAt(2)) {
iterator.remove();
}
}
}
2 ball 일 때 메커니즘은 아래 예시와 같다.
123 2 ball
x12 빼고 다 삭제인데 근데 X자리에는 3이 오면 안 되니깐 X가 3오는 수 삭제
x31 빼고 다 삭제인데 X자리에 1 이오는 수 삭제
x32
2x1
3x1
3x2
21x
31x
23x
public void twoB(String number,Iterator<String> iterator) {
while (iterator.hasNext()) {
String nextNum = iterator.next();
if(!number.substring(0, 2).equals(nextNum.substring(1))) {
if(number.charAt(0) != nextNum.charAt(2)|| number.charAt(2) != nextNum.charAt(1)) {
if(number.charAt(1) != nextNum.charAt(2) || number.charAt(2) != nextNum.charAt(1)) {
if(number.charAt(1) != nextNum.charAt(0) || number.charAt(0) != nextNum.charAt(2)) {
if(number.charAt(2) != nextNum.charAt(0) || number.charAt(0) != nextNum.charAt(2)) {
if(number.charAt(2) != nextNum.charAt(0) || number.charAt(1) != nextNum.charAt(2)) {
if(number.charAt(1) != nextNum.charAt(0) || number.charAt(0) != nextNum.charAt(1)) {
if(number.charAt(2) != nextNum.charAt(0) || number.charAt(0) != nextNum.charAt(1)) {
if(number.charAt(1) != nextNum.charAt(0) || number.charAt(2) != nextNum.charAt(1)) {
iterator.remove();
}else if(number.charAt(0) == nextNum.charAt(2)) {
iterator.remove();
}
}else if(number.charAt(1) == nextNum.charAt(2)) {
iterator.remove();
}
}else if(number.charAt(2) == nextNum.charAt(2)) {
iterator.remove();
}
}else if(number.charAt(0) == nextNum.charAt(1)) {
iterator.remove();
}
}else if(number.charAt(1) == nextNum.charAt(1)) {
iterator.remove();
}
}else if(number.charAt(2) == nextNum.charAt(1)) {
iterator.remove();
}
}else if(number.charAt(0) == nextNum.charAt(0)) {
iterator.remove();
}
}else if(number.charAt(1) == nextNum.charAt(0)){
iterator.remove();
}
}else if(number.charAt(2) == nextNum.charAt(0)) {
iterator.remove();
}
}
}
3 BALL일 때 메커니즘은 그 숫자들을 가지고 있지 않으면 나머지 수들을 삭제하는 것이다.
ex) 123 3 ball일 때
1,2,3을 가지고 있지 않는 모든 수들을 제거합니다..
하지만 이 메커니즘은 수정이 필요할 것 같습니다.
123일 때 스트라이크로 만들 수 있는 경우의 수는 312 231 밖에 없는데 위 메커니즘은 쓰면 원하는 답을 찾는데 시간이 더 거릴 거 같습니다.
public void threeB(String number,Iterator<String> iterator) {
while (iterator.hasNext()) {
String nextNum = iterator.next();
if(!nextNum.contains(number.substring(0, 1))) {
iterator.remove();
}else if(!nextNum.contains(number.substring(1, 2))) {
iterator.remove();
}else if(!nextNum.contains(number.substring(2, 3))) {
iterator.remove();
}
}
}
1 Strike일 때 메커니즘은 세 숫자 중에 하나는 반드시 숫자도 맞고 자리도 맡는다는 말이니깐 아래 예시와 같이 삭제한다.
Ex) 123 1 strike
1xx
x2x
xx3
위 숫자 모양을 가진 숫자들 빼고 다 삭제
public void oneS(String number,Iterator<String> iterator) {
while (iterator.hasNext()) {
String nextNum = iterator.next();
if(number.charAt(0) != nextNum.charAt(0)) {
if(number.charAt(1) != nextNum.charAt(1)) {
if(number.charAt(2) != nextNum.charAt(2)) {
iterator.remove();
}else if(nextNum.substring(0,2).contains(number.substring(0, 1))||nextNum.substring(0,2).contains(number.substring(1, 2))) {
iterator.remove();
}
}else if(nextNum.substring(0,1).contains(number.substring(0, 1))||nextNum.substring(0,1).contains(number.substring(2, 3))) {
iterator.remove();
}else if(nextNum.substring(2,3).contains(number.substring(0, 1))||nextNum.substring(2,3).contains(number.substring(2, 3))) {
iterator.remove();
}
}else if(nextNum.substring(1).contains(number.substring(1, 2))||nextNum.substring(1).contains(number.substring(2, 3))) {
iterator.remove();
}
}
}
2 Strike일 때 메커니즘은
123 2 strike
12x
x23
1x3
위 3 경우의 패턴을 가진 수가 아닐 경우 다 삭제 즉, 위 패턴인 수만 경우의 수로 살린다.
public void twoS(String number,Iterator<String> iterator) {
while (iterator.hasNext()) {
String nextNum = iterator.next();
if(number.charAt(0) != nextNum.charAt(0) || number.charAt(1) != nextNum.charAt(1)) {
if(number.charAt(1) != nextNum.charAt(1) || number.charAt(2) != nextNum.charAt(2)) {
if(number.charAt(0) != nextNum.charAt(0) || number.charAt(2) != nextNum.charAt(2)){
iterator.remove();
}
}
}
}
}
1 Strike 1 Ball일 때 메커니즘은
Ex) 123 1s 1b
1x2x자리에는 이제 더 이상 3은 절대 올 수 없게 된다
13x x자리에는 이제 더 이상 2는 절대 올 수 없게 된다
x21x자리에는 이제 더 이상 3은 절대 올 수 없게 된다
32x
2x3
x13
위경우를 성립하지 않는 번호를 다 삭제했다.+ x자리에 남는 번호는 절대 올 수 없다.
public void oneSOneB(String number,Iterator<String> iterator) {
while (iterator.hasNext()) {
String nextNum = iterator.next();
if(number.charAt(0) != nextNum.charAt(0) || number.charAt(1) != nextNum.charAt(2)) {
if(number.charAt(0) != nextNum.charAt(0) || number.charAt(2) != nextNum.charAt(1)) {
if(number.charAt(1) != nextNum.charAt(1) || number.charAt(0) != nextNum.charAt(2)) {
if(number.charAt(1) != nextNum.charAt(1) || number.charAt(2) != nextNum.charAt(0)) {
if(number.charAt(2) != nextNum.charAt(2) || number.charAt(1) != nextNum.charAt(0)) {
if(number.charAt(2) != nextNum.charAt(2) || number.charAt(0) != nextNum.charAt(1)) {
iterator.remove();
}else if(number.charAt(1) == nextNum.charAt(0)) {
iterator.remove();
}
}else if(number.charAt(0) == nextNum.charAt(1)) {
iterator.remove();
}
}else if(number.charAt(0) == nextNum.charAt(2)) {
iterator.remove();
}
}else if(number.charAt(2) == nextNum.charAt(0)) {
iterator.remove();
}
}else if(number.charAt(1) == nextNum.charAt(2)) {
iterator.remove();
}
}else if(number.charAt(2) == nextNum.charAt(1)) {
iterator.remove();
}
}
}
1 Strike 2 Ball 일 때 메커니즘은 3 ball이랑 비슷하다 그 숫자들을 안 가지고 있으면 삭제이다.
ex) 123이면 1,2,3을 가지지 않는 숫자들은 다 삭제이다
근데 사실 이 메커니즘도 3 ball와 마찬가지로 수정이 필요할 것 같다 만약 123이 1 s2 b 이면 3 strike 경 우의는 132 321 213밖에 경우의 수밖에 안 남는다. 그게 사실 내 코드처럼 삭제하면 저 3개만 남는 거 같지 않아서 수정이 필요할 것 같다는 생각이 들었다.
public void oneSTwoB(String number,Iterator<String> iterator) {
while (iterator.hasNext()) {
String nextNum = iterator.next();
if(!nextNum.contains(number.substring(0, 1))) {
iterator.remove();
}else if(!nextNum.contains(number.substring(1, 2))) {
iterator.remove();
}else if(!nextNum.contains(number.substring(2, 3))) {
iterator.remove();
}
}
}
0 Strike 0 Ball일 경우는 out이라고 메서드명을 지정했다 출루했다 라는 느낌이다.
메커니즘은 3 ball과 반대로 그 수를 가지고 있는 숫자는 다 삭제다.
ex) 123이 0 strike 0 ball 이면 1,2,3을 가진수 전부 삭제.
public void out(String number,Iterator<String> iterator) {
while (iterator.hasNext()) {
String nextNum = iterator.next();
if(nextNum.contains(number.substring(0, 1))) {
iterator.remove();
}else if(nextNum.contains(number.substring(1, 2))) {
iterator.remove();
}else if(nextNum.contains(number.substring(2, 3))) {
iterator.remove();
}
}
}
3 Strike 일 때는 삼진이니깐 그냥 삼진 됐다고 출력시키고 경기 종료시키면 된다
public void threeS(String number,Iterator<String> iterator) {
System.out.println(number+" : !!!삼진!!!");
System.out.println("count : "+ cnt); //몇번만에 맞췄는지 확인하기 위해서
ystem.out.println("-----------------------------------------------------------");
System.out.println();
}
코드를 실행시키면 아래와 같은 결과처럼 나온다.


그리고 반복문을 통해서 1000번의 게임을 실행했을 때 한게임을 이기는데 걸리는 횟수는 평균이 5가 나왔다.
비록 알고리즘을 교수님께서 힌트를 주셔서 힌트를 바탕으로 짜긴 했지만 시행착오가 많았다. 처음에 코드 돌렸을 때 한 게임당 숫자를 찾는 횟수가 10 이상 나왔는데 팀원들의 머리를 한대 모아 열심히 짠 결과 9-> 8-> 7 ->6->5 수정할 때마다 횟수가 하나씩 줄어나갔다. 비록 코드가 길어서 비효율적으로 보일 수도 있지만 인터넷을 안 보고 메모장에 경우의 수를 일일이 확인해 가며 알고리즘을 짤 수 있다는 게 좋았다. 혼자 했으면 빠르게 포기했을 것 같은 코드들이지만 서로 멘탈 케어하며 열심히 한 거 같다.
'자료구조and알고리즘' 카테고리의 다른 글
| [알고리즘] DFS 깊이 우선 탐색 (Stack 사용)미로 (0) | 2022.07.15 |
|---|---|
| [알고리즘] Stack 이해하기 직접만들기+ Stack<E>사용하기 (0) | 2022.07.14 |
| [알고리즘] Binary Search and Recursive (0) | 2022.07.13 |
| [알고리즘]BabyGin 판단 문제(순열 사용) (0) | 2022.07.12 |
| [Java]알고리즘 문제 2번 (0) | 2022.07.11 |
댓글