0

I'm implementing a card game in java, however i cannot expand efficiently the game tree in order for computer to execute MiniMax algorithm on it, here is the code:


public void BuildTree(Stato State,int depth)
    { 
       if(depth < 0)
       {
           throw new IllegalArgumentException();
       }
       
       if(depth == 0)
       {
           State.Results = null;
       }
       
       if(depth > 0)
       {
           for(int i=0; i<depth; i++)
           {
               State.GeneraSuccessore();
               
               System.out.println("SIZE: "+SizeOfTree(State));
               
               for(Stato S : State.Results)
               {
                   S.GeneraSuccessore();
               }
           }
       }
       
       DecisionTree = State;
           
       PrintAlbero();
    }

This is the structure of a State (Stato.java). Each State contains:

  • Cards in the hand of the CPU
  • Cards on the table
  • Current CPU score
  • A label
  • A KnowledgeBase in order to know stats about cards such as probability and gain for each move
  • A boolean indicating if its the turn of Computer (player MAX) or Player (player MIN)
public class Stato 
{   
    /*----CONFIGURAZIONE RELATIVA AD UNO STATO----*/
    KnowledgeBase Actual;
    
    ArrayList<Carta> Table;
    ArrayList<Carta> Hand;
    
    ArrayList<Carta> Opponent;
    
    Gioco Game;
    
    Punteggio Score;
    
    boolean IsMax;
    
    String Label;
    
    double Gain;
    
    /*----FINE CONFIGURAZIONE RELATIVA AD UNO STATO----*/
    
    /*----IMPLEMENTAZIONE DEI NODI PER L'USO DELLA RICORSIONE----*/
    Stato Parent;
    
    ArrayList<Stato> Results;
    /*----FINE IMPLEMENTAZIONE DEI NODI PER L'USO DELLA RICORSIONE----*/
    
    public Stato(String L,ArrayList<Carta> T,ArrayList<Carta> H,KnowledgeBase K,Punteggio S,boolean Q)
    {
        Actual = K;
        
        Table = T;
        
        Hand = H;
        
        Label = L;

        Score = S;
        
        IsMax = Q;
        
        Parent = null;
        
        Gain = 0;
        
        Results = new ArrayList();
    }  
    
    public void GeneraSuccessore()
    {
        boolean turn = !IsMax;

        if(!turn)//CASO MAX (TURNO CPU)
        {
            for(Carta C : Hand)
            {
                if(C.IsMarked() && !Table.isEmpty())
                {                
                    for(int Sc = 1; Sc <=C.Potenziale.size(); Sc++)
                    {
                       if(C.HasPotential(Sc))
                       {
                            Stato S1;
                            S1 = new Stato(Label+"-> "+C.nome+"/"+Sc,FT(Table,C,Sc),FH(Hand,C),FKB(Actual,C),FS(Score,C),turn);
                            S1.Gain = C.ValoriPotenziale.get(Sc);
                            
                            Results.add(S1);
                       }
                    }
                }
                else
                {
                    Stato S1;
                    S1 = new Stato(Label+"-> "+C.nome+"/"+0,FT(Table,C,1),FH(Hand,C),FKB(Actual,C),FS(Score,C),turn); 
                    S1.Gain = 0.0;
                    
                    
                    Results.add(S1);
                }
            }
        }
        else//CASO MIN (TURNO GIOCATORE)
        {
            GetOpponent();
            for(Carta C : Opponent)
            {
                if(C.IsMarked() && !Table.isEmpty())
                {                
                    for(int Sc = 1; Sc <=C.Potenziale.size(); Sc++)
                    {
                       if(C.HasPotential(Sc))
                       {
                           Stato S1;
                            S1 = new Stato(Label+"-> "+C.nome+"/"+Sc,FT(Table,C,Sc),FH(Opponent,C),FKB(Actual,C),FS(Score,C),turn);
                            S1.Gain = C.ValoriPotenziale.get(Sc);
                            
                            Results.add(S1);

                       }

                    }
                }
                else
                {
                    Stato S1;
                    
                    S1 = new Stato(Label+"-> "+C.nome+"/"+0,FT(Table,C,1),FH(Opponent,C),FKB(Actual,C),FS(Score,C),turn); 
                    S1.Gain = 0.0;
                    
                    Results.add(S1);
                }
            }
        }

    }
       
    public ArrayList<Carta> FT(ArrayList<Carta> T,Carta C,int Scelta)
    {
        if(C.HasPotential(Scelta))
        {
            ArrayList<Carta> TB = new ArrayList();
        
            TB.addAll(T);
        
            for(Carta C1 : C.Potenziale.get(Scelta))
            {
                TB.remove(C1);
            }
            
            return TB;
        }
        else
        {
            ArrayList<Carta> TB = new ArrayList();
            
            TB.addAll(T);
            TB.add(C);
            
            return TB;
        }
    }
        
    public ArrayList<Carta> FH(ArrayList<Carta> H,Carta C)
    {
        ArrayList<Carta> HND = new ArrayList();
        
        HND.addAll(H);
        
        HND.remove(C);

        return HND;
    }
    
    public KnowledgeBase FKB(KnowledgeBase K,Carta C)
    {
        KnowledgeBase KB1 = K;
        
        KB1.RimuoviCarta(C);
        
        return KB1;
    }
    
    public Punteggio FS(Punteggio S,Carta C)
    {
        Punteggio SCR = S;
        
        S.AddCard(C);
        
        return SCR;
    }
    

    public void SetKB(KnowledgeBase KB)
    {
        Actual = KB;
    }
    
    public void SetTable(ArrayList<Carta> Tv)
    {
        Table = Tv;
    }
    
    public void SetHand(ArrayList<Carta> KnownHand)
    {
        Hand = KnownHand;
    }
    
    public KnowledgeBase GetKB()
    {
        return Actual;
    }
    
    public ArrayList<Carta> GetTable()
    {
        return Table;
    }
    
    public ArrayList<Carta> GetHand()
    {
        return Hand;
    }
    
    public ArrayList<Carta> GetOpponent()
    {
        Opponent = Actual.GetMostValuableCards(Table,Score);
        return Opponent;
    }
    
    public Stato AddResult(Stato Res) 
    {
        Res.SetParent(this);
        this.Results.add(Res);
        return Res;
    }
 
    public void AddResultsList(ArrayList<Stato> ListOfStates) 
    {
        for(Stato Res : ListOfStates)
        {
            Res.SetParent(this);
        }
            
        Results.addAll(ListOfStates);
    }

    public ArrayList<Stato> GetResults()
    {
        return Results;
    }
 
    protected void SetParent(Stato Previous) 
    {
        this.Parent = Previous;
    }
 
    public Stato GetParent() 
    {
        return Parent;
    }
    
    public void SetLabel(String L)
    {
        Label = L;
    }
    
    public String GetLabel()
    {
        return Label;
    }
    
    public void SetPunteggio(Punteggio S)
    {
        Score = S;
    }
    
    public Punteggio GetPunteggio()
    {
        return Score;
    }
    
    public void ResetGain()
    {
        Gain = 0.0;
    }
    
    public void SetGain(Double D)
    {
        Gain = D;
    }
    
    public double GeiGain()
    {
        return Gain;
    }
    
    public void UpdateState(KnowledgeBase KB, ArrayList<Carta> Tb,ArrayList<Carta> Hnd)
    {
        Actual = KB;
        Table = Tb;
        Hand = Hnd;
    }
}

The problem is that when i call the BuildTree method, JVM stops working, i think its because this way of building a game tree is quite too time and resource-consuming.

How would you solve this?

EnricoT94
  • 19
  • 2
  • Variable and field names should start in lowercase, so they don't get confused with class names, which start in uppercase. – Progman Jul 25 '20 at 17:08
  • 1
    Few coding-style tips: 1) What @Progman told you above. 2) Curly brackets open at the same line, not in the next one. 3) It is always good practice to write your code in English, you never know who will put their hands on it so it should be globally understandable - I understand it here because I am Italian, but probably other people will find it hard to understand the logic if they can't understand what the variables or methods represent literally and will give up helping you because of the effort it takes to understand. – Matteo NNZ Jul 25 '20 at 17:48
  • Apart for the tips, the question is not really clear. What is the bug you're finding, where is it happening and how can we reproduce it? – Matteo NNZ Jul 25 '20 at 17:49
  • Sorry but the question is still too large. Reading the method `buildTree()` at first sight it doesn't look to be consuming. However, you're not sharing some classes / methods so the problem may be there and not visible without the code. Also, you can try to set breakpoints on each line of the method `buildTree()` and figure out on which line the memory starts to be eaten. Without a reproducible test case, it's hard to navigate your code and figure out where the issue is. – Matteo NNZ Jul 25 '20 at 18:17
  • If you're trying to enumerate *all* probabilities then your time and memory usage would grow exponentially (factorially). I suggest you use a strategy based on creating [`Spliterator`](https://docs.oracle.com/javase/8/docs/api/java/util/Spliterator.html) implementation which you could wrap in a stream. Article about Spliterators [here](https://blog.hcf.dev/article/2019-03-28-java-streams-and-spliterators/) and a companion about evaluating poker hands [here](https://blog.hcf.dev/article/2019-10-29-java-enums-as-predicates/). – Allen D. Ball Jul 25 '20 at 21:14
  • @MatteoNNZ What classes should i write in my question? – EnricoT94 Jul 26 '20 at 16:58
  • @EnricoT94 ideally all those functions which are called in your method. Also, how your method is called (I mean which parameters does it receive). But generally speaking, this is a huge debugging task and doesn't fit the Q&A format of Stack Overflow. I guess you should debug on your own and try to reduce the use case to something easy to reproduce and small enough to fit here. – Matteo NNZ Jul 26 '20 at 17:17

0 Answers0