diff --git a/src/main/java/controls/BotIntelligence.java b/src/main/java/controls/BotIntelligence.java new file mode 100644 index 0000000..903b489 --- /dev/null +++ b/src/main/java/controls/BotIntelligence.java @@ -0,0 +1,114 @@ +package controls; + +import interfaces.IAttack; +import interfaces.IBotBehavior; + +import java.util.*; + +import static views.Constants.DECK_COUNT; + +public class BotIntelligence implements IBotBehavior { + + public BotIntelligence(){ + + } + /** + 未確定の数字を列挙する + */ + List calculateCandidate(IAttack iAttack){ + //候補の初期化 + var candidates = new ArrayList(); + for(int i=0;i x.getKey()).toList()); + candidates.removeAll(iAttack.getMyHands().stream().filter(x -> x.getValue()).map(x -> x.getKey()).toList()); + + return candidates; + } + + /** + * 相手の手札に対して、候補となる数字を割り当てる + * @param iAttack + * @return 候補のリスト + */ + List assignCandidateNumberEachHand(IAttack iAttack){ + var myHands=iAttack.getMyHands(); + var result=new ArrayList(); + + for(var card:myHands){//ユーザーの手札を左から見ていく + var index=myHands.indexOf(card);//左からindex番目に + var candidateList = calculateCandidate(iAttack);//確認するカード + + + if(card.getValue()) { + candidateList.clear(); + }else{ //裏の場合 + + //そのカードから右隣のカードを確認していく + for(int i=0;i x > currentCard.getKey());//その確認したカードの数字より大きい数字を除外する。 + for(int j=1;j x x).toArray()); + } + return result; + } + + //公開されていないカードのインデックスを選ぶ(カードにかかれている番号ではない) + @Override + public int selectAttacker(IAttack iAttack) { + var botHands = iAttack.getOpponentHands(); + + for(var i:botHands){//小さいものから選択 + if(!i.getValue())return botHands.indexOf(i); + } + return 0; + } + + @Override + public int selectTarget(IAttack iAttack) { + var candidate= assignCandidateNumberEachHand(iAttack); + int result=0; + int min=DECK_COUNT; + for(var i:candidate){ + if(i.length> getMyHands() { - return isATurn?algo.getHandsA():algo.getHandsB(); + return algo.getHandsA(); } @Override public List> getOpponentHands() { - return isATurn?algo.getHandsB():algo.getHandsA(); + return algo.getHandsB(); } @Override @@ -58,10 +59,23 @@ public boolean isDeckLess(){ return algo.getDeck().size()==0; } + + @Override + public int getDeckNumber() { + return algo.getDeck().size(); + } + public boolean isSucceedLatestAttack(){ return isSucceedLatestAttack; } - public boolean getIsATurn(){ + + @Override + public boolean shiftTurn() { + isATurn = isATurn?false:true; + return isATurn; + } + + public boolean isATurn(){ return isATurn; } } diff --git a/src/main/java/interfaces/IAttack.java b/src/main/java/interfaces/IAttack.java index ccca842..fbc06f1 100644 --- a/src/main/java/interfaces/IAttack.java +++ b/src/main/java/interfaces/IAttack.java @@ -15,5 +15,8 @@ Map.Entry getTopCard(); boolean isDeckLess(); + int getDeckNumber(); boolean isSucceedLatestAttack(); + boolean shiftTurn(); + boolean isATurn(); } diff --git a/src/main/java/interfaces/IBotBehavior.java b/src/main/java/interfaces/IBotBehavior.java new file mode 100644 index 0000000..7000961 --- /dev/null +++ b/src/main/java/interfaces/IBotBehavior.java @@ -0,0 +1,7 @@ +package interfaces; + +public interface IBotBehavior { + int selectAttacker(IAttack iAttack); + int selectTarget(IAttack iAttack); + int declareNumber(IAttack iAttack, int target); +} diff --git a/src/main/java/views/MainFrame.java b/src/main/java/views/MainFrame.java index cc1077e..fdd34fa 100644 --- a/src/main/java/views/MainFrame.java +++ b/src/main/java/views/MainFrame.java @@ -5,6 +5,8 @@ import javax.swing.*; import java.awt.*; +import java.awt.event.WindowEvent; +import java.awt.event.WindowListener; public class MainFrame extends JFrame { StepScheduler stepScheduler; @@ -18,5 +20,41 @@ mainPanel.setVisible(true); this.add(mainPanel, BorderLayout.CENTER); this.pack(); + this.addWindowListener(new WindowListener() { + @Override + public void windowOpened(WindowEvent e) { + mainPanel.playerBehave(stepScheduler); + } + + @Override + public void windowClosing(WindowEvent e) { + + } + + @Override + public void windowClosed(WindowEvent e) { + + } + + @Override + public void windowIconified(WindowEvent e) { + + } + + @Override + public void windowDeiconified(WindowEvent e) { + + } + + @Override + public void windowActivated(WindowEvent e) { + + } + + @Override + public void windowDeactivated(WindowEvent e) { + + } + }); } } diff --git a/src/main/java/views/MainPanel.java b/src/main/java/views/MainPanel.java index c7c7ae1..1e53747 100644 --- a/src/main/java/views/MainPanel.java +++ b/src/main/java/views/MainPanel.java @@ -1,5 +1,7 @@ package views; +import controls.BotIntelligence; +import controls.ConstantMethods; import controls.StepScheduler; import interfaces.IAttack; @@ -9,6 +11,7 @@ import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collections; import java.util.List; @@ -42,22 +45,45 @@ opponentHandButtons = new ArrayList<>(); selectableOpponentHandKeys = new ArrayList<>(); currentStep = Step.SelectMyHands; - setButtons(stepScheduler); - } - - void setButtons(IAttack stepScheduler) { deckButtonPanel = new JPanel(); myHandButtonsPanel = new JPanel(); opponentButtonsPanel = new JPanel(); + //setButtons(stepScheduler); + if(!stepScheduler.isDeckLess()){ //デッキが存在する場合 + var top=stepScheduler.getTopCard(); + var cardButton =new JButton("deck"); + cardButton.setPreferredSize(new Dimension(CARD_HEIGHT,CARD_WIDTH)); + deckButtonPanel.add(cardButton); + } + repaintField(stepScheduler); + /** + * setButton末尾にあった処理をコンストラクタ内へ。 + * + */ + + add(deckButtonPanel,BorderLayout.WEST); + + //add(myHandButtonsPanel, BorderLayout.SOUTH); + add(myHandButtonsPanel, BorderLayout.SOUTH); + + add(opponentButtonsPanel, BorderLayout.NORTH); + setStep(Step.SelectOpponentHands); + repaint(); + // if(stepScheduler.isATurn())playerBehave(stepScheduler); + // else botBehave(stepScheduler); + } + + void setButtons(IAttack iAttack) { + // myHandButtonsPanel.setLayout(new BorderLayout()); // myHandButtonsPanel.setLayout(null); - var deckTopCard=stepScheduler.getTopCard(); - var myHands = stepScheduler.getMyHands(); - var opponentHands = stepScheduler.getOpponentHands(); + var deckTopCard=iAttack.getTopCard(); + var myHands = iAttack.getMyHands(); + var opponentHands = iAttack.getOpponentHands(); isDecidedAttacker=false; - if(!stepScheduler.isDeckLess()){ //デッキが存在する場合にデッキトップのカードを引く処理 + if(!iAttack.isDeckLess()){ //デッキが存在する場合にデッキトップのカードを引く処理 var cardButton =new CardButton(deckTopCard.getKey().toString()); cardButton.setBounds(0,100,CARD_WIDTH,CARD_HEIGHT); @@ -68,9 +94,9 @@ } - if(!stepScheduler.isDeckLess()){ //デッキが存在する場合 + if(!iAttack.isDeckLess()){ //デッキが存在する場合 - var top=stepScheduler.getTopCard(); + var top=iAttack.getTopCard(); var cardButton =new JButton("deck"); cardButton.setPreferredSize(new Dimension(CARD_HEIGHT,CARD_WIDTH)); deckButtonPanel.add(cardButton); @@ -111,11 +137,13 @@ indexForOpponent=0; for (var i : opponentHands) { var cardButton = new CardButton(i.getValue() ? i.getKey().toString() :CLOSED_SYMBOL); + cardButton.setStatus(i.getValue()? CardButton.Status.OPEN: CardButton.Status.CLOSED); cardButton.setEnabled(false); cardButton.addActionListener(new ActionListener() { final int index=indexForOpponent; public void actionPerformed(ActionEvent e) { + if(!iAttack.isDeckLess())isDecidedAttacker=true; if(!isDecidedAttacker){ JOptionPane.showMessageDialog(null, "Select Attacker from Your Hands. ", "Warn", JOptionPane.WARNING_MESSAGE); @@ -144,22 +172,28 @@ if(getDeclaredNumber!=null) { //数字を宣言して、承認したとき guess = Integer.parseInt(getDeclaredNumber); var g= Integer.parseInt(getDeclaredNumber); - stepScheduler.Attack(g, attacker, t); + iAttack.Attack(g, attacker, t); setStep(Step.Confirm); - boolean isSucceed=stepScheduler.isSucceedLatestAttack(); + boolean isSucceed=iAttack.isSucceedLatestAttack(); String resultMessage="Your Attack Was "; resultMessage+=isSucceed?"Succeed.":"Failed."; JOptionPane.showMessageDialog(null,resultMessage); //次の画面に遷移する処理 - update(stepScheduler); + update(iAttack); + + }else{ setStep(Step.SelectOpponentHands); } + } cardButton.setEnabledSelection(false); + Timer timer = new Timer(1000, this); + timer.setDelay(6000); + botBehave(iAttack); } }); opponentHandButtons.add(cardButton); @@ -175,7 +209,7 @@ add(opponentButtonsPanel, BorderLayout.NORTH); setStep(Step.SelectOpponentHands); - + // update(stepScheduler); //試行錯誤用 ->やりたいことはできた // myHandButtonsPanel.removeAll(); // for(var mh : myHandButtons){ @@ -204,8 +238,44 @@ break; } } + void botBehave(IAttack iAttack){ + iAttack.shiftTurn(); + var bot = new BotIntelligence(); + + JOptionPane.showMessageDialog(null,"Bot`s Turn."); + var selectText = ""; + var atk =0; + if(!iAttack.isDeckLess()){ //デッキにカードが存在するとき + paintDrawCard(iAttack);//デッキから引いたカードを描画する + selectText = "Bot drew."; + }else{ + atk= bot.selectAttacker(iAttack); + selectText = "Bot selected. "; + } + JOptionPane.showMessageDialog(null,selectText); + + var targetText = ""; + var tar = bot.selectTarget(iAttack); + JOptionPane.showMessageDialog(null,"Bot targeted this."); + + + var dec = bot.declareNumber(iAttack, tar); + JOptionPane.showMessageDialog(null,"Bot declared "+ dec +"."); + + iAttack.Attack(dec, atk, tar); + boolean isSucceed=iAttack.isSucceedLatestAttack(); + + String resultMessage="Bot`s Attack Was "; + resultMessage+=isSucceed?"Succeed.":"Failed."; + JOptionPane.showMessageDialog(null,resultMessage); + + repaintField(iAttack); + iAttack.shiftTurn(); + playerBehave(iAttack); + } void update(IAttack iAttack){ - myHandButtonsPanel.removeAll(); + myHandButtonsPanel.removeAll();// + myHandButtons.clear(); //選択可能な自分のカードのキーを取得 selectableMyHandKeys = iAttack.getMyHands().stream(). filter(x -> !x.getValue()).//その中から非公開のカードを抽出 @@ -216,30 +286,109 @@ // if (!selectableMyHandKeys.contains(i.getKey())) cardButton.setEnabled(false); myHandButtons.add(cardButton); myHandButtonsPanel.add(cardButton, BorderLayout.WEST); + final var a=indexForMyHands; cardButton.addActionListener(new ActionListener() { + @Override public void actionPerformed(ActionEvent e) { + //if(currentStep!=Step.SelectMyHands)return; cardButton.setEnabledSelection(true); var option = JOptionPane.showConfirmDialog(null, "Attack with This Card?", "confirmation", 2); cardButton.setEnabledSelection(false); + if (option == JOptionPane.YES_OPTION){ + attacker=a; + isDecidedAttacker=true; + } } }); } - myHandButtons.stream().forEach(x -> { - // x.setEnabled(false); - }); + opponentButtonsPanel.removeAll();// + opponentHandButtons.clear(); + //選択可能な相手のカードのキーを取得 + selectableOpponentHandKeys = iAttack.getOpponentHands().stream(). + filter(x -> !x.getValue()).//その中から非公開のカードを抽出 + map(x -> x.getKey()).toList();//カードのリストからキーのリストへ変換 + indexForOpponent = 0; + for (var i : iAttack.getOpponentHands()) { - //opponentHandButtons.clear(); - //remove(opponentButtonsPanel); - for(int i=0,n=opponentHandButtons.size();i{ // // var cardButton = new JButton(x.getValue() ? x.getKey().toString() :CLOSED_SYMBOL); @@ -251,7 +400,176 @@ opponentHandButtons.stream().forEach(x -> { x.setEnabled(true); }); + + + isDecidedAttacker=false; // add(opponentButtonsPanel); - /// repaint(); + validate(); + repaint(); + } + void paintDrawCard(IAttack iAttack){ + var deckTopCard=iAttack.getTopCard(); + + if(!iAttack.isDeckLess()){ //デッキが存在する場合にデッキトップのカードを表示する処理 + var cardButton =new CardButton(deckTopCard.getKey().toString()); + cardButton.setBounds(0,100,CARD_WIDTH,CARD_HEIGHT); + if(iAttack.isATurn()){ + myHandButtons.add(cardButton); + myHandButtonsPanel.add(cardButton, BorderLayout.WEST); + isDecidedAttacker=true; + }else { + cardButton.setText(CLOSED_SYMBOL); + opponentHandButtons.add(cardButton); + opponentButtonsPanel.add(cardButton, BorderLayout.WEST); + } + if(iAttack.getDeckNumber()==1){ + deckButtonPanel.removeAll(); + } + } + + validate(); + repaint(); + } + void repaintField(IAttack iAttack){ + + var myHands = iAttack.getMyHands(); + var opponentHands = iAttack.getOpponentHands(); + + /** + * 初期化処理(する必要があるのかどうかは知らない) + */ + isDecidedAttacker=false; + myHandButtonsPanel.removeAll();// + myHandButtons.clear(); + opponentButtonsPanel.removeAll();// + opponentHandButtons.clear(); + + + /** + * 自分の手札に関する処理 + */ + indexForMyHands=0; + for (var i : myHands) { + var cardButton = new CardButton(i.getKey().toString()); + cardButton.setStatus(i.getValue()? CardButton.Status.OPEN: CardButton.Status.MY_CLOSED); + myHandButtons.add(cardButton); + myHandButtonsPanel.add(cardButton, BorderLayout.WEST); + + final var a=indexForMyHands;//actionPerformedの中に書くと、クリックされて初めて、回しきったindexForMyHandsを参照してしまうため、ここで一時変数に格納する + cardButton.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + if(!iAttack.isDeckLess())return;//デッキがあるときは何も反応しないように + + cardButton.setEnabledSelection(true); + var option = JOptionPane.showConfirmDialog(null, "Attack with This Card?", "confirmation", 2); + + cardButton.setEnabledSelection(false); + if (option == JOptionPane.YES_OPTION){ + attacker=a; + isDecidedAttacker=true; + } + } + }); + indexForMyHands++; + } + //ここまでが自分のカードに関する処理 + + /** + *相手のカードに関する処理 + */ + indexForOpponent=0; + for (var i : opponentHands) { + var cardButton = new CardButton(i.getValue() ? i.getKey().toString() :CLOSED_SYMBOL); + + cardButton.setStatus(i.getValue()? CardButton.Status.OPEN: CardButton.Status.CLOSED); + cardButton.setEnabled(!i.getValue()); + cardButton.addActionListener(new ActionListener() { + final int index=indexForOpponent; + public void actionPerformed(ActionEvent e) { + if(!iAttack.isDeckLess())isDecidedAttacker=true; + if(!isDecidedAttacker){ + JOptionPane.showMessageDialog(null, "Select Attacker from Your Hands. ", "Warn", + JOptionPane.WARNING_MESSAGE); + return; + } + + cardButton.setEnabledSelection(true); + + //相手のカードを選択したときに確認用ダイアログを出す + var option = JOptionPane.showConfirmDialog(null, "Select This Card?", "confirmation", 2); + if (option == JOptionPane.YES_OPTION) { + target = opponentHands.size()-index;//画面上,相手の手札も自分の手札と同じように左から右へ並べられているため,それを補正するために反転させている + var t = index; + setStep(Step.Declare); + String[] optionsToChoose = new String[DECK_COUNT]; + for (var i = 0; i < optionsToChoose.length; i++) optionsToChoose[i] = String.valueOf(i); + var getDeclaredNumber = (String) JOptionPane.showInputDialog( + null, + "What number is this card?", + "Declare Number", + JOptionPane.QUESTION_MESSAGE, + null, + optionsToChoose, + optionsToChoose[0]); + if(getDeclaredNumber!=null) { //数字を宣言して、承認したとき + guess = Integer.parseInt(getDeclaredNumber); + var g= Integer.parseInt(getDeclaredNumber); + iAttack.Attack(g, attacker, t); + setStep(Step.Confirm); + + boolean isSucceed=iAttack.isSucceedLatestAttack(); + + String resultMessage="Your Attack Was "; + resultMessage+=isSucceed?"Succeed.":"Failed."; + JOptionPane.showMessageDialog(null,resultMessage); + + //次の画面に遷移する処理 + //repaintField(iAttack); + + + }else{ + setStep(Step.SelectOpponentHands); + return; + } + + }else{ + cardButton.setEnabledSelection(false); + return; + } + cardButton.setEnabledSelection(false); + repaintField(iAttack); + + botBehave(iAttack); + } + }); + opponentHandButtons.add(cardButton); + opponentButtonsPanel.add(cardButton, 0);//見た目の順序が逆になるように,0番目に挿入 + indexForOpponent++; + } + + + + //ここまでが相手のカードに関する処理 + validate(); + repaint(); + } + + public void playerBehave(IAttack iAttack){ + isDecidedAttacker=false; + JOptionPane.showMessageDialog(null,"Your Turn."); + var selectText = ""; + var atk =0; + if(!iAttack.isDeckLess()){ //デッキにカードが存在するとき + selectText = "You drew \""+ iAttack.getTopCard().getKey()+"\"."; + }else{ + selectText = "Select Card used in Your Attack from Your Hands."; + } + paintDrawCard(iAttack); + + JOptionPane.showMessageDialog(null,selectText); + + if(!iAttack.isDeckLess())JOptionPane.showMessageDialog(null,"Select Card from Opponent`s Hands."); + } } diff --git a/src/test/java/AlgoTest.java b/src/test/java/AlgoTest.java index 7d6c2c2..76f97bb 100644 --- a/src/test/java/AlgoTest.java +++ b/src/test/java/AlgoTest.java @@ -3,14 +3,15 @@ import java.util.AbstractMap; +import static controls.ConstantMethods.printAlgoState; import static org.junit.jupiter.api.Assertions.*; -class AlgoTest { +public class AlgoTest { @Test void ゲーム開始時0から11のカードで構成されたデッキとそこから2枚引いて構成された各プレイヤーの手札が存在する() { Algo algo = new Algo(); - printState(algo); + printAlgoState(algo); assertEquals(2, algo.getHandsA().size()); assertEquals(2, algo.getHandsB().size()); } @@ -28,7 +29,7 @@ //algo.drawA(guess, target); algo.inputDrawA(guess,target); - printState(algo); + printAlgoState(algo); //Aはデッキから引いているので,実行前と比べて枚数が1枚増えているか assertEquals(iniHandsANum+1,algo.getHandsA().size()); //Aによってアタックを成功されたBのカードが表になっているか @@ -50,7 +51,7 @@ //テスト対象メソッド // algo.drawA(guess, target); algo.inputDrawA(guess, target); - printState(algo); + printAlgoState(algo); //Aはデッキから引いているので,実行前と比べて枚数が1枚増えている assertEquals(iniHandsANum+1,algo.getHandsA().size()); @@ -74,7 +75,7 @@ //テスト対象メソッド // algo.drawB(guess, target); algo.inputDrawB(guess, target); - printState(algo); + printAlgoState(algo); //Aはデッキから引いているので,実行前と比べて枚数が1枚増えているか assertEquals(iniHandsBNum+1,algo.getHandsB().size()); //Aによってアタックを成功されたBのカードが表になっているか @@ -95,7 +96,7 @@ //テスト対象メソッド //algo.selectA(guess, attacker, target); algo.inputSelectA(guess, attacker, target); - printState(algo); + printAlgoState(algo); //Aによってアタックを成功されたBのカードが表になっているか assertEquals(true, algo.getHandsB().get(target).getValue()); //Aのアタックを成功しているので,使用したカードが表になっていないか @@ -113,7 +114,7 @@ //テスト対象メソッド // algo.selectA(guess, attacker, target); algo.inputSelectA(guess, attacker, target); - printState(algo); + printAlgoState(algo); //Aによってアタックを成功されたBのカードが表になっていないか assertEquals(false, algo.getHandsB().get(target).getValue()); //Aのアタックが失敗しているので,使用したカードが表になっているか @@ -153,27 +154,7 @@ algo.inputDrawA(gue, tar); printViewA(algo); } - - - void printState(Algo algo){ - String s=""; - s+="deck:\n"; - for(var i : algo.getDeck()){ - s+="\t"+i.getKey()+"\t"+(i.getValue()?"open":"closed")+"\n"; - } - s+="handsA:\n"; - for(var i : algo.getHandsA()){ - s+="\t"+i.getKey()+"\t"+(i.getValue()?"open":"closed")+"\n"; - } - s+="handsB:\n"; - for(var i : algo.getHandsB()){ - s+="\t"+i.getKey()+"\t"+(i.getValue()?"open":"closed")+"\n"; - } - s+="\n"; - if(algo.getLoseA())s+="B win.\n"; - else if(algo.getLoseB())s+="A win.\n"; - System.out.println(s); - } + void printViewA(Algo algo){ String s="--------------\n"; diff --git a/src/test/java/controls/BotIntelligenceTest.java b/src/test/java/controls/BotIntelligenceTest.java new file mode 100644 index 0000000..44e99ba --- /dev/null +++ b/src/test/java/controls/BotIntelligenceTest.java @@ -0,0 +1,43 @@ +package controls; + +import org.junit.jupiter.api.Test; + +import static controls.ConstantMethods.printAlgoState; +import static org.junit.jupiter.api.Assertions.assertEquals; + + +class BotIntelligenceTest { + + @Test + void 正しく未確定数字を計算ができる() { + StepScheduler stepScheduler=new StepScheduler(); + BotIntelligence botIntelligence=new BotIntelligence(); + var algo = stepScheduler.algo; + stepScheduler.isATurn = false; + var candidate=botIntelligence.calculateCandidate(stepScheduler); + printAlgoState(algo); + var s= "["; + for (var i : candidate)s+=i+" "; + s+="]"; + System.out.println(s); + } + @Test + void 各手札のカードに対して候補を算出できる(){ + StepScheduler stepScheduler=new StepScheduler(); + BotIntelligence botIntelligence=new BotIntelligence(); + var algo = stepScheduler.algo; + + var iniHandsANum = algo.getHandsA().size(); + var initDeckNum=algo.getDeck().size(); + //正解のカードを選択させるための変数 + var guess = algo.getHandsB().get(0).getKey(); + var target = 1; + //テスト対象メソッド + + algo.inputDrawA(guess,target); + + stepScheduler.isATurn = false; + printAlgoState(algo); + botIntelligence.assignCandidateNumberEachHand(stepScheduler); + } +} \ No newline at end of file