package Cluedo.g1;

class ClueSuperBroomSolver extends ClueBroomSolver {
    CountingLogic alogic = null;
    int[] world;
    int[][][] last_quest;
    boolean[] quested;

    public ClueSuperBroomSolver( int n, int p, int c, int[] holds ) {
        super( n, p, c, holds );

        if ( p > 2 )
            alogic = new CountingLogic( p+1, n, logic._hc, logic._vc );
        world = new int[n];
        for ( int i=0; i<n; i++ )
            world[i] = i;

        last_quest = new int[p][p][];
        for ( int i=0; i<p; i++ )
            for ( int j=0; j<p; j++ )
                last_quest[i][j] = null;

        quested = new boolean[p];
    }
    
    void owns( int idx, int[] cards, int n1, int n2 ) {
        super.owns( idx, cards, n1, n2 );
        if ( alogic == null )
            return;

        if ( !alogic.add( idx, cards, n1, n2 ) )
        {
            //System.out.print( "Warning: alogic wrong in adding [" + idx + "] " 
                              //+ n1 + "-" + n2 );
            //printcards( cards );
            //alogic.printstates();
            
            alogic = null;
        }

        //alogic.printstates();
    }

    public void observe( int from, int to, int[] list, boolean response ) {
        if ( null == alogic )
            return;
        if ( !response )
        {
            for ( int i=0; i<p(); i++ )
                quested[i] = true;
            return;
        }

        //System.out.print( "Warning: observing " + from + " asking " + to 
         //                 + " for a list: " );
        //printcards( list );

        if ( from == 0 )
            return;

        if ( !quested[from] )
        {
            int[] diff = alogic.difference( world, world.length,
                                            list, list.length );
            if ( null != diff && diff.length <= c() )
            {
                //System.out.print( "*** suppose player " + from + " has cards " );
                //printcards( diff );
                if ( !alogic.add( from, diff, diff.length, diff.length ) )
                {
                    //System.out.println( "alogic error!" );
                    //alogic.printstates();
                }
            }

            quested[from] = true;
        }
/*
        try
        {
            int[] diff = alogic.difference( world, world.length, 
                                            list, list.length );
            if ( null == diff )
                return;

            System.out.print( "The diff is" );
            printcards( diff );
            

            Clue clue = alogic.reduce( to, diff, diff.length,
                                       0, list.length, false );

            list = ClueProblem.resizeArray( clue.list, clue.n );

            if ( null == alogic.difference( last_quest[from][to], list ) )
            {
                System.out.print( "*** Adding additional clue: [" 
                                  + to + "] " 
                                  + "1-" + list.length );
                printcards( list );
                
                if ( !alogic.add( to, list, 1, list.length ) )
                {
                    System.out.println( "alogic error!" );
                    alogic.printstates();
                }
            }

            last_quest[from][to] = list;
        }
        catch ( InvalidClueException e ) 
        {
            alogic = null;
        }
*/
    }

    int[] quest( int idx ) {
        if ( null == alogic )
            return super.quest( idx );

        int[] ret = new int[n()];
        int j=0;

        if ( _unknown_players > 1 )
        {
            for ( int i=0; i<n(); i++ )
                if ( alogic.state( idx, i ) == 0 
                     || ( alogic.state( idx, i ) < 0 
                          && _rand.nextInt(4) != 0 ) )
                    ret[j++] = i;
        }
        else
        {
            int count = 0;
            for ( int i=0; i<n(); i++ )
                if ( alogic.state( idx, i ) == 0 )
                {
                    if ( count++ < k() )
                        ret[j++] = i;
                }
                else if( alogic.state( idx, i ) < 0 
                         && _rand.nextInt(4) != 0 ) {
                    ret[j++] = i;
                }
        }

        return resizeArray( ret, j );
    }

    double rate() {
        if ( null == alogic )
            return super.rate();

        int[] cnt = alogic.countrow( p() );

        int total = n() - cnt[0] - cnt[1];
        int choose = k() - cnt[0];
        double combinations = binomial( total, choose );

        return 1.0 / combinations;
    }
    

    int[] guess() {
        if ( null == alogic )
            return super.guess();

        int[] ret = new int[k()];
        int j=0;

        for ( int i=0; i<n(); i++ )
            if ( alogic.state( p(), i ) > 0 )
            {
                ret[j++] = i;
                if ( j >= ret.length )
                    return ret;
            }

        for ( int i=0; i<n(); i++ )
            if ( alogic.state( p(), i ) == 0 )
            {
                ret[j++] = i;
                if ( j >= ret.length )
                    return ret;
            }

        // this cannot happen.
        for ( int i=0; i<n(); i++ )
            if ( alogic.state( p(), i ) < 0 )
            {
                ret[j++] = i;
                if ( j >= ret.length )
                    return ret;
            }

        return ret;

        // throw new RuntimeException( "Program internal error" );
    }


    /** find a best player to ask a question
     */
    int who() {
        if ( null == alogic )
            return super.who();

        int p = p();
        int n = n();
        int unknown = 0;

        int[] count = new int[p];
        for ( int i=1; i<p; i++ )
        {
            boolean flag = false;
            for ( int j=0; j<n; j++ )
                if ( 0 == alogic.state( i, j ) )
                {
                    count[i]++;
                    flag = true;
                }

            if ( flag )
                unknown++;
        }

        _unknown_players = unknown;

        int ret = 0;
        int c = 0;
        for ( int i=1; i<p; i++ )
            if ( i != _doom_player && count[i] > 0 && count[i] >= c )
            {
                ret = i;
                c = count[i];
            }

        if ( 0 == ret )
            ret = _doom_player;
        return ret;
    }
}
