基礎から学んだJavaでゲームを作るその２ 数字当てゲーム

この記事は[JavaとPythonを基礎から学びたい私のための Advent Calendar 2024](https://qiita.com/advent-calendar/2024/aruhitonocalendar)の12日目の記事です。ほぼヒットアンドブローを作りました。
# ゲーム制作2日目
昨日は入力を受け取る部分を作ったので、今日は数字の判定なども含め仕上げていきます。さっさと完成したゲームが見たい方のために、最初に完成したゲームのコード全体を載せておきます。本来ならば、入力した文字に重複がないかチェックを入れるべきですが、その程度は入力時に自分でチェックすればいいのですっ飛ばしています。
```java
import java.util.Scanner;
import java.util.zip.ZipEntry;
import java.util.Random;
public class Suujiate {
    public static boolean T=false;
    public static String A;
    public static int E;
    public static void main(String[] args)  {
    int H=0;
    int BL=0;
    if(T==false){
          E=RndFour();
        
    }
    
    System.out.println("4 Number?");
    
    Scanner scanner=new java.util.Scanner(System.in);
    A=scanner.nextLine();
    boolean C=B(A);

    if(C==true)
    {  String G=Integer.toString(E);
        
         for(int i=0;i<A.length();i++){
            char J=A.charAt(i);
            if(G.contains(Character.toString(J))){
                if(G.charAt(i)==J){
                    H++;
                }
                else{
                    BL++;
                }  
            }
        }
        System.out.println("Hit:"+H+"Blow"+BL);
        T=true;
        if(H==4){
        System.out.println("Finish!");
        }else{
            main(args);
        }
    }
    else{
        System.out.println("SUUJI!");
    }
   
    

    }
    public static int RndFour(){
        E=new Random().nextInt(8999)+1000;
        int[] RndE={0,0,0,0};
        String RndSe="";
        int Naka=0;
        boolean HanteiA=false;
        for(int i=0;;i++){
            int RndF=new Random().nextInt(9);
            for(int j=0;j<4;j++){
                if(RndE[j]==RndF){
                    
                    HanteiA=true;
                    break;
                }
            }
            if(HanteiA==false){
                RndE[Naka]=RndF;
                RndSe=RndSe+RndF;
                Naka++;
            }else{
                HanteiA=false;
            }
            if(Naka==4){
                break;
            }
           
        }
        
        
        int RndB=Integer.parseInt(RndSe);

        return RndB;





    }
    public static Boolean B(String O){
       for(int i=0;i<O.length();i++){
        if(Character.isDigit(O.charAt(i))){
     
            continue;
        }
        return false;
       }
       return true;
    }
    
}
```
# 解説
ここからが本題です。
まず、仕組みとして
①ランダムに重複しない４桁の数を作る
②数字の入力を受け付ける
③判定をする
④正しければ終わり、間違っていたら②から再開
といった流れです。
昨日、②についての作り方を書いたのでそれ以外について解説します
## ランダムに重複しない数を作る
```
    public static int RndFour(){
        E=new Random().nextInt(8999)+1000;
        int[] RndE={0,0,0,0};
        String RndSe="";
        int Naka=0;
        boolean HanteiA=false;
        for(int i=0;;i++){
            int RndF=new Random().nextInt(9);
            for(int j=0;j<4;j++){
                if(RndE[j]==RndF){
                    
                    HanteiA=true;
                    break;
                }
            }
            if(HanteiA==false){
                RndE[Naka]=RndF;
                RndSe=RndSe+RndF;
                Naka++;
            }else{
                HanteiA=false;
            }
            if(Naka==4){
                break;
            }
           
        }
        
        
        int RndB=Integer.parseInt(RndSe);

        return RndB;
    }
```
方法は様々あるようですが、今回は配列を使った方法を取りました。(他がうまくできなかったため)
```
 int[] RndE={0,0,0,0};
```
まず、４つの数字が入った配列を用意します。配列内の要素は数字の位に対応しています。もし空の配列にしてしまうと、この後の重複しているかどうか確認する処理でエラーを吐くため、とりあえず0を入れています。あとで変わります。
```
 for(int i=0;;i++){
            int RndF=new Random().nextInt(9);
            for(int j=0;j<4;j++){
                if(RndE[j]==RndF){
                    
                    HanteiA=true;
                    break;
                }
            }
```
核となる部分です。最初のforは無限ループになっています。重複しない数字が出るまで処理が続くためです。2行目のRndFにランダムに1から9までの数を入れて、4行目以降で先ほどの配列の中の要素とかぶっていないかを判定します。かぶっていた場合には判定用に用意しておいたbooleanなHanteiAがtrueになり、即座に次の処理に移ります。
```
    if(HanteiA==false){
                RndE[Naka]=RndF;
                RndSe=RndSe+RndF;
                Naka++;
            }else{
                HanteiA=false;
            }
            if(Naka==4){
                break;
            }
           
        }
```
先ほどの判定で見事重複なしだった場合は、配列のRndEに、端から順に数字が入っていきます。また、StringなRndSeに文字として数字が入っていきます。配列内の数字がすべて置き換わると無限ループが終了します。
```
int RndB=Integer.parseInt(RndSe);

        return RndB;
``` 
StringなRndSeをIntegerなRndBに変えて、戻り値に設定してあげます。
次に、判定の部分の開設をします
## 判定
```
 if(C==true)
    {  String G=Integer.toString(E);
        
         for(int i=0;i<A.length();i++){
            char J=A.charAt(i);
            if(G.contains(Character.toString(J))){
                if(G.charAt(i)==J){
                    H++;
                }
                else{
                    BL++;
                }  
            }
        }
        System.out.println("Hit:"+H+"Blow"+BL);
```
一文字ずつ確認するため、char型が便利です。まず、char型の入力した文字１つが、ToStringで文字列に変換した正解の数字とかぶっているかを判定します。その後、さらに入力された場所と正解の数字の場所が一致しているかを確認しています。
よくわかるif文の詳細です⇩
```
if(G.contains(Character.toString(J))){
                if(G.charAt(i)==J){
                    H++;
                }
                else{
                    BL++;
                }  
```
入力場所（位）と正解の場所(位)が一致している場合はH、つまりHitを増やしています。そうでない場合はBlowを増やしています。
# 終わるか再開するかの処理
```
 T=true;
        if(H==4){
        System.out.println("Finish!");
        }else{
            main(args);
        }
    }
    else{
        System.out.println("SUUJI!");
    }
```
最初のTは、再開する際に毎回処理をしてほしくないものを処理しないようにするためのものです。一番最初のほうに、if(T==true)で囲まれたものがあると思います。例えばこのゲームの正解です。毎回変わってしまうとゲームとして成り立たなくなるため、初回だけ動くような処理をしています。
# 今日学んだこと　
## 乱数
上のものと内容は重複しますが、Javaでの乱数の発生の仕方です。
```
  int RndF=new Random().nextInt(9);
```
通常はこの形でランダムに返してくれるようです。一番最後の()に入っている数字は上限値で、この場合0から9までの数字を返してくれます
マイナスを出してほしいときは
```
  int RndF=new Random().nextInt(9)-10;
```
といった感じでやると出せるそうです

## 数字を重複させない方法
配列を使った方法もありますが、ほかにも割り算の整数部分を使った方法など様々なものがあるようです。
# まとめ
Javaの乱数は、基本の形では正の数を出すことができる。出てきた数から数字をマイナスしないと負の数を出すことができない。調べた限りでは乱数を重複なしで生成する場合はコードを書いていく必要があり、Randomクラスにそのような機能はない。

# 参考文献

https://www.sejuku.net/blog/22308

https://ict-skillup.com/java/1995/

https://magazine.techacademy.jp/magazine/17733