/*
 * Copyright 2008 nori090
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.coderepos.nori090.df;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Queue;
import java.util.Set;
import java.util.TreeSet;

import org.coderepos.nori090.df.Card.TYPE;
import org.coderepos.nori090.df.Command.CAST;
import org.coderepos.nori090.df.Command.DEAL;
import org.coderepos.nori090.df.Command.PLAY;
import org.coderepos.nori090.df.Command.START;
import org.coderepos.nori090.df.Command.TURN;

/**
 * @author nori090
 * @version $Rev: 228 $ $Date: 2008-11-03 16:00:30 +0900 (Mon, 03 Nov 2008) $
 */
public class DF
    extends DFAbst {

    GameCaptureData data = null;

    public DF( String name ) {
        super( name );
    }

    @Override
    void init() {
    }

    Queue<TYPE[]> 俺ターンQueue = null;

    @Override
    void start( START start ) {
        data = new GameCaptureData( this );
        data.start( start );
        俺ターンQueue = new LinkedList<TYPE[]>();
    }

    @Override
    void play( PLAY play ) {
        String name = getPlayers()[play.getNumber()].getName();
        if ( isTrace() || isDebug() ) {
            foutln( name + ":" + Arrays.toString( play.getCard().list ) );
        }
        data.play( play );
    }

    @Override
    void cast( CAST cast ) {
        data.cast( cast );
    }

    /**
     * ランクが１位か２位（カード交換するものを選べる権利がある場合）のみ呼び出されます。 <BR>
     * １位の場合は２枚、２位の場合は１枚のカードを選びCASTコマンドで返す。
     */
    @Override
    CAST deal( DEAL deal ) {
        TYPE[] c = deal.getDeal().list;
        Arrays.sort( c, Card.getPowerComparator( true, isRevolve ) );
        CAST cast = null;
        if ( deal.getRank() == 1 ) {
            try {
                TYPE[] select = new TYPE[2];
                select[0] = c[0];
                int base = c[0].intValue();
                for ( TYPE t : c ) {
                    if ( t.intValue() != base ) {
                        select[1] = t;
                        break;
                    }
                }
                cast = new CAST( new Card( select ), "ｸﾞﾇﾇﾇﾇ" );
            }
            catch ( DFException e ) {
                throw new RuntimeException( e );
            }
        }
        else if ( deal.getRank() == 2 ) {
            try {
                cast = new CAST( new Card( c[0] ), "あざーす＾＾" );
            }
            catch ( DFException e ) {
                throw new RuntimeException( e );
            }
        }
        return cast;
    }

    Set<TYPE> makeMap( Card c ) {
        TreeSet<TYPE> set = new TreeSet<TYPE>( Card.getPowerComparator( true, false ) );
        for ( TYPE t : c.list ) {
            set.add( t );
        }
        return set;
    }

    @Override
    Command.PLAY turn( Command.TURN turn ) {
        // 保守率の算出
        data.turn( turn );
        Set<TYPE> set = makeMap( turn.getMyCards() );
        Arrays.sort( turn.getMyCards().list, Card.getPowerComparator( true, turn.isRevolve ) );
        // 前に出されたカードがジョーカー１枚で、自分がスペードの３を持っているか。
        if ( turn.getLast().equals( TYPE.FF ) && set.contains( TYPE.S3 ) ) {
            return select( TYPE.S3, "そうはい神崎！ぼくぁ、永遠に不滅です。" );
        }
        // 最後に場に出されたカードがパス
        if ( turn.getLast().equals( TYPE.PP ) ) {
            if ( !俺ターンQueue.isEmpty() ) {
                return select( 俺ターンQueue.poll(), "勝ちフラグきたこれ！" );
            }
            else {
                int check = checkずっと俺のターン( turn, data.getHoldAllCards() );
                if ( check > 0 ) {
                    return select( 俺ターンQueue.poll(), "勝ちフラグきたこれ！" );
                }
                return select( getBest( turn, set ), "粛清の・・始まりだァーーーッ" );
            }
        }

        Command.PLAY selected = select( new TYPE[] { TYPE.PP }, "パスだよ！・・・ﾁﾈ" );
        if ( turn.getLast().list.length == 1 ) {
            selected = getBestCard( turn, set );
        }
        else {
            selected = getBestNcard( turn, turn.getLast().list.length, set );
        }
        return selected;
    }

    /**
     * @param turn
     * @param all_cards 場に出てない残カード
     * @param set 手持ちカードのセット
     * @return
     */
    private int checkずっと俺のターン( TURN turn, Set<TYPE> all_cards ) {
        boolean isRevolve = turn.isRevolve();
        TYPE[] cards = new TYPE[turn.getMyCards().list.length];
        System.arraycopy( turn.getMyCards().list, 0, cards, 0, turn.getMyCards().list.length );
        Set<TYPE> set = new HashSet<TYPE>();
        set.addAll( Arrays.asList( cards ) );
        if ( isTrace() || isDebug() ) {
            foutln( String.format( "手持ちカード：%s", Arrays.toString( cards ) ) );
        }
        int max = 0;
        for ( TYPE t : all_cards ) {
            max = Math.max( max, t.intValue( isRevolve ) );
        }
        if ( isTrace() || isDebug() ) {
            foutln( String.format( "場に出てないカード：%s", Arrays.toString( all_cards.toArray( new TYPE[] {} ) ) ) );
        }
        Set<TYPE> over_set = new LinkedHashSet<TYPE>();
        Set<TYPE> over8_set = new LinkedHashSet<TYPE>();
        Set<TYPE> test = new HashSet<TYPE>( set );
        for ( TYPE t : cards ) {
            if ( t.intValue( isRevolve ) >= max ) {
                over_set.add( t );
                test.remove( t );
            }
            if ( t.intValue( isRevolve ) == TYPE.S8.intValue( isRevolve ) ) {
                over8_set.add( t );
                test.remove( t );
            }
        }
        if ( isTrace() || isDebug() ) {
            foutln( String.format( "手持ちで場を切れるカード（単体）：%s", Arrays.toString( over_set.toArray( new TYPE[] {} ) ) ) );
        }
        if ( over_set.size() == 0 ) {
            return -1;
        }
        if ( TYPE.S8.intValue( isRevolve ) >= max ) {
            over_set.remove( TYPE.C8 );
            over_set.remove( TYPE.D8 );
            over_set.remove( TYPE.H8 );
            over_set.remove( TYPE.S8 );
        }

        if ( test.size() == 1 ) {
            if ( ( over_set.size() + over8_set.size() + 1 ) == cards.length ) {
                for ( TYPE t : over_set ) {
                    俺ターンQueue.add( new TYPE[] { t } );
                }
                for ( TYPE t : over8_set ) {
                    俺ターンQueue.add( new TYPE[] { t } );
                }
                for ( TYPE t : test ) {
                    俺ターンQueue.add( new TYPE[] { t } );
                }
                return max;
            }
        }
        else {
            int power = 0;
            boolean check = true;
            for ( TYPE t : test ) {
                if ( power == 0 ) {
                    power = t.intValue( isRevolve );
                }
                else if ( power != t.intValue( isRevolve ) ) {
                    check = false;
                    break;
                }
            }
            if ( check ) {
                if ( ( over_set.size() + over8_set.size() + test.size() ) == cards.length ) {
                    for ( TYPE t : over_set ) {
                        俺ターンQueue.add( new TYPE[] { t } );
                    }
                    for ( TYPE t : over8_set ) {
                        俺ターンQueue.add( new TYPE[] { t } );
                    }
                    俺ターンQueue.add( test.toArray( new TYPE[] {} ) );
                    return max;
                }
            }
        }

        Set<Integer> over2set = new LinkedHashSet<Integer>();
        Map<Integer, Integer> myself2 = getGroupMap( test.toArray( new TYPE[] {} ), 2, false );
        Map<Integer, Integer> yourself2 =
            getGroupMap( all_cards.toArray( new TYPE[] {} ), 2, all_cards.contains( TYPE.FF ) );
        int yourself2max = 0;
        for ( Integer power : yourself2.keySet() ) {
            yourself2max = Math.max( yourself2max, power.intValue() );
        }
        if ( isTrace() || isDebug() ) {
            foutln( String.format( "場に出てないペア最大有効ポイント：%d", yourself2max ) );
        }
        for ( Integer power : myself2.keySet() ) {
            if ( power >= yourself2max ) {
                over2set.add( power );
            }
        }
        if ( isTrace() || isDebug() ) {
            foutln( String.format( "手持ちで場を切れるカード（ペア）：%s", Arrays.toString( over2set.toArray( new Integer[] {} ) ) ) );
        }

        Set<Integer> over3set = new LinkedHashSet<Integer>();
        Map<Integer, Integer> myself3 = getGroupMap( test.toArray( new TYPE[] {} ), 3, false );
        Map<Integer, Integer> yourself3 =
            getGroupMap( all_cards.toArray( new TYPE[] {} ), 3, all_cards.contains( TYPE.FF ) );
        int yourself3max = 0;
        for ( Integer power : yourself3.keySet() ) {
            yourself3max = Math.max( yourself3max, power.intValue() );
        }
        if ( isTrace() || isDebug() ) {
            foutln( String.format( "場に出てないトリプル最大有効ポイント：%d", yourself3max ) );
        }
        for ( Integer power : myself3.keySet() ) {
            if ( power >= yourself3max ) {
                over3set.add( power );
            }
        }
        if ( isTrace() || isDebug() ) {
            foutln( String.format( "手持ちで場を切れるカード（トリプル）：%s", Arrays.toString( over3set.toArray( new Integer[] {} ) ) ) );
        }

        for ( Integer power : over3set ) {
            over2set.remove( power );
        }
        if ( isTrace() || isDebug() ) {
            foutln( String.format( "手持ちで場を切れるカード（補正後ペア）：%s", Arrays.toString( over2set.toArray( new Integer[] {} ) ) ) );
        }

        int over_count = 0;
        over_count += ( over3set.size() * 3 );
        over_count += ( over2set.size() * 2 );

        if ( isTrace() || isDebug() ) {
            foutln( String.format( "俺のターンで続けられる枚数（単体カード）：%d", over_set.size() ) );
            foutln( String.format( "俺のターンで続けられる枚数（８切カード）：%d", over8_set.size() ) );
            foutln( String.format( "俺のターンで続けられる枚数（連続カード）：%d", over_count ) );
        }

        if ( ( over_set.size() + over8_set.size() + over_count + 1 ) >= cards.length ) {
            for ( TYPE t : over_set ) {
                俺ターンQueue.add( new TYPE[] { t } );
            }
            for ( TYPE t : over8_set ) {
                俺ターンQueue.add( new TYPE[] { t } );
            }
            for ( Integer base : over2set ) {
                TYPE[] ts = new TYPE[2];
                int index = 0;
                for ( TYPE t : test.toArray( new TYPE[] {} ) ) {
                    if ( t.intValue( isRevolve ) == base ) {
                        if ( index == 1 ) {
                            ts[index] = t;
                            test.remove( t );
                            break;
                        }
                        else {
                            ts[index] = t;
                            test.remove( t );
                            index++;
                        }
                    }
                }
                俺ターンQueue.add( ts );
            }
            for ( Integer base : over3set ) {
                TYPE[] ts = new TYPE[3];
                int index = 0;
                for ( TYPE t : test.toArray( new TYPE[] {} ) ) {
                    if ( t.intValue( isRevolve ) == base ) {
                        if ( index == 2 ) {
                            ts[index] = t;
                            test.remove( t );
                            break;
                        }
                        else {
                            ts[index] = t;
                            test.remove( t );
                            index++;
                        }
                    }
                }
                俺ターンQueue.add( ts );
            }
            俺ターンQueue.add( test.toArray( new TYPE[] {} ) );
            return max;
        }

        // 手持ちにＪＫがある場合のオプション
        if ( set.contains( TYPE.FF ) && over_set.size() != 1 ) {
            Map<Integer, Integer> m2 = getGroupMap( test.toArray( new TYPE[] {} ), 2, set.contains( TYPE.FF ) );
            for ( Integer power : m2.keySet().toArray( new Integer[] {} ) ) {
                int count = m2.get( power ).intValue();
                if ( count != 1 || power.intValue() < yourself2max ) {
                    m2.remove( power );
                }
            }
            Map<Integer, Integer> m3 = getGroupMap( test.toArray( new TYPE[] {} ), 3, set.contains( TYPE.FF ) );
            for ( Integer power : m3.keySet().toArray( new Integer[] {} ) ) {
                int count = m3.get( power ).intValue();
                if ( count != 2 || power.intValue() < yourself3max ) {
                    m3.remove( power );
                }
            }
            if ( ( m2.size() + m3.size() ) == 1 ) {
                Set<TYPE> test2 = new HashSet<TYPE>( test );
                for ( Integer power : m2.keySet() ) {
                    int delete = 0;
                    for ( TYPE t : test2.toArray( new TYPE[] {} ) ) {
                        if ( power == t.intValue( isRevolve ) ) {
                            if ( delete == 1 ) {
                                test2.remove( t );
                                break;
                            }
                            else {
                                test2.remove( t );
                                delete++;
                            }
                        }
                    }
                }
                for ( Integer power : m3.keySet() ) {
                    int delete = 0;
                    for ( TYPE t : test2.toArray( new TYPE[] {} ) ) {
                        if ( power == t.intValue( isRevolve ) ) {
                            if ( delete == 2 ) {
                                test2.remove( t );
                                break;
                            }
                            else {
                                test2.remove( t );
                                delete++;
                            }
                        }
                    }
                }

                over_count += ( m2.size() * 2 );
                over_count += ( m3.size() * 3 );
                if ( test2.size() == 1 ) {
                    if ( ( over_set.size() + over8_set.size() + over_count + 1 ) == cards.length ) {
                        for ( TYPE t : over_set ) {
                            俺ターンQueue.add( new TYPE[] { t } );
                        }
                        for ( TYPE t : over8_set ) {
                            俺ターンQueue.add( new TYPE[] { t } );
                        }
                        for ( Integer base : over2set ) {
                            TYPE[] ts = new TYPE[2];
                            int index = 0;
                            for ( TYPE t : test.toArray( new TYPE[] {} ) ) {
                                if ( t.intValue( isRevolve ) == base ) {
                                    if ( index == 1 ) {
                                        ts[index] = t;
                                        test.remove( t );
                                        break;
                                    }
                                    else {
                                        ts[index] = t;
                                        test.remove( t );
                                        index++;
                                    }
                                }
                            }
                            俺ターンQueue.add( ts );
                        }
                        for ( Integer base : over3set ) {
                            TYPE[] ts = new TYPE[3];
                            int index = 0;
                            for ( TYPE t : test.toArray( new TYPE[] {} ) ) {
                                if ( t.intValue( isRevolve ) == base ) {
                                    if ( index == 2 ) {
                                        ts[index] = t;
                                        test.remove( t );
                                        break;
                                    }
                                    else {
                                        ts[index] = t;
                                        test.remove( t );
                                        index++;
                                    }
                                }
                            }
                            俺ターンQueue.add( ts );
                        }
                        for ( Integer power : m2.keySet() ) {
                            TYPE[] ts = new TYPE[2];
                            int index = 0;
                            for ( TYPE t : test.toArray( new TYPE[] {} ) ) {
                                if ( t.intValue( isRevolve ) == power ) {
                                    if ( index == 1 ) {
                                        ts[index] = t;
                                        test.remove( t );
                                        break;
                                    }
                                    else {
                                        ts[index] = t;
                                        test.remove( t );
                                        index++;
                                    }
                                }
                            }
                            俺ターンQueue.add( ts );
                        }
                        for ( Integer power : m3.keySet() ) {
                            TYPE[] ts = new TYPE[3];
                            int index = 0;
                            for ( TYPE t : test.toArray( new TYPE[] {} ) ) {
                                if ( t.intValue( isRevolve ) == power ) {
                                    if ( index == 2 ) {
                                        ts[index] = t;
                                        test.remove( t );
                                        break;
                                    }
                                    else {
                                        ts[index] = t;
                                        test.remove( t );
                                        index++;
                                    }
                                }
                            }
                            俺ターンQueue.add( ts );
                        }
                        俺ターンQueue.add( test.toArray( new TYPE[] {} ) );
                        return max;
                    }
                }
                else {
                    int p = 0;
                    boolean check = true;
                    for ( TYPE t : test2 ) {
                        if ( p == 0 ) {
                            p = t.intValue( isRevolve );
                        }
                        else if ( p != t.intValue( isRevolve ) ) {
                            check = false;
                            break;
                        }
                    }
                    if ( check ) {
                        if ( ( over_set.size() + over8_set.size() + over_count + test2.size() ) == cards.length ) {
                            for ( TYPE t : over_set ) {
                                俺ターンQueue.add( new TYPE[] { t } );
                            }
                            for ( TYPE t : over8_set ) {
                                俺ターンQueue.add( new TYPE[] { t } );
                            }
                            for ( Integer base : over2set ) {
                                TYPE[] ts = new TYPE[2];
                                int index = 0;
                                for ( TYPE t : test.toArray( new TYPE[] {} ) ) {
                                    if ( t.intValue( isRevolve ) == base ) {
                                        if ( index == 1 ) {
                                            ts[index] = t;
                                            test.remove( t );
                                            break;
                                        }
                                        else {
                                            ts[index] = t;
                                            test.remove( t );
                                            index++;
                                        }
                                    }
                                }
                                俺ターンQueue.add( ts );
                            }
                            for ( Integer base : over3set ) {
                                TYPE[] ts = new TYPE[3];
                                int index = 0;
                                for ( TYPE t : test.toArray( new TYPE[] {} ) ) {
                                    if ( t.intValue( isRevolve ) == base ) {
                                        if ( index == 2 ) {
                                            ts[index] = t;
                                            test.remove( t );
                                            break;
                                        }
                                        else {
                                            ts[index] = t;
                                            test.remove( t );
                                            index++;
                                        }
                                    }
                                }
                                俺ターンQueue.add( ts );
                            }
                            for ( Integer power : m2.keySet() ) {
                                TYPE[] ts = new TYPE[2];
                                int index = 0;
                                for ( TYPE t : test.toArray( new TYPE[] {} ) ) {
                                    if ( t.intValue( isRevolve ) == power ) {
                                        if ( index == 1 ) {
                                            ts[index] = t;
                                            test.remove( t );
                                            break;
                                        }
                                        else {
                                            ts[index] = t;
                                            test.remove( t );
                                            index++;
                                        }
                                    }
                                }
                                俺ターンQueue.add( ts );
                            }
                            for ( Integer power : m3.keySet() ) {
                                TYPE[] ts = new TYPE[3];
                                int index = 0;
                                for ( TYPE t : test.toArray( new TYPE[] {} ) ) {
                                    if ( t.intValue( isRevolve ) == power ) {
                                        if ( index == 2 ) {
                                            ts[index] = t;
                                            test.remove( t );
                                            break;
                                        }
                                        else {
                                            ts[index] = t;
                                            test.remove( t );
                                            index++;
                                        }
                                    }
                                }
                                俺ターンQueue.add( ts );
                            }
                            俺ターンQueue.add( test.toArray( new TYPE[] {} ) );
                            return max;
                        }
                    }
                }
            }
        }
        return -1;
    }

    private Command.PLAY getBestCard( TURN turn, Set<TYPE> set ) {
        int lastpower = turn.getLast().list[0].intValue( isRevolve );
        boolean has8 =
            set.contains( TYPE.C8 ) || set.contains( TYPE.D8 ) || set.contains( TYPE.H8 ) || set.contains( TYPE.S8 );
        boolean hasJ = set.contains( TYPE.FF );
        if ( lastpower < 8 && has8 ) {
            return select( getEquivalenceTypes( turn.getMyCards(), 8, 1 )[0], "上上下下左右左右BA" );
        }
        if ( data.hosyu > 50 && hasJ && !data.getHoldAllCards().contains( TYPE.S3 ) ) {
            return select( TYPE.FF, "よーしパパ特盛頼んじゃうぞー" );
        }
        List<TYPE> nogroup = getNoGroupCard( turn, set.contains( TYPE.FF ) );
        if ( !nogroup.isEmpty() ) {
            for ( TYPE t : nogroup ) {
                if ( lastpower < t.intValue( isRevolve ) ) {
                    TYPE[] select = getEquivalenceTypes( turn.getMyCards(), t.intValue( isRevolve ), 1 );
                    if ( select.length != turn.getMyCards().list.length ) {
                        if ( select[0].intValue( isRevolve ) < 14 ) {
                            return select( select[0], "うんちくらえ！" );
                        }
                    }
                }
            }
        }
        for ( TYPE t : turn.getMyCards().list ) {
            if ( lastpower < t.intValue( isRevolve ) ) {
                TYPE[] select = getEquivalenceTypes( turn.getMyCards(), t.intValue( isRevolve ), 1 );
                if ( select.length != turn.getMyCards().list.length ) {
                    if ( select[0].intValue( isRevolve ) < 14 ) {
                        return select( select[0], "くそう、手が無いよ＞＜" );
                    }
                }
            }
        }
        return select( TYPE.PP, "ダディークール！" );
    }

    List<TYPE> getNoGroupCard( TURN turn, boolean hasJK ) {
        List<Integer> group = getGroupList( turn, hasJK );
        LinkedHashSet<Integer> set = new LinkedHashSet<Integer>();
        for ( Integer power : group ) {
            set.add( power );
        }
        ArrayList<TYPE> list = new ArrayList<TYPE>();
        for ( TYPE t : turn.getMyCards().list ) {
            if ( !set.contains( t.intValue( isRevolve ) ) ) {
                list.add( t );
            }
        }
        return list;
    }

    private Command.PLAY getBestNcard( TURN turn, int num, Set<TYPE> set ) {
        int lastpower = getLastPower( turn.getLast().list );
        Map<Integer, Integer> map = getGroupMap( turn.getMyCards().list, num, set.contains( TYPE.FF ) );
        for ( Integer power : map.keySet() ) {
            if ( lastpower < power.intValue() ) {
                TYPE[] select = getEquivalenceTypes( turn.getMyCards(), power, num );
                if ( turn.getMyCards().list.length != select.length ) {
                    if ( select.length == 4 && !isRevolve ) {
                        select = new TYPE[] { select[0], select[1] };
                    }
                    if ( select[0].intValue( isRevolve ) >= 13 ) {
                        break;
                    }
                }
                return select( select, "あまいぞ！" );
            }
        }
        return select( new TYPE[] { TYPE.PP }, "カードねぇよ！" );
    }

    private int getLastPower( TYPE[] list ) {
        for ( TYPE t : list ) {
            if ( t != TYPE.FF ) {
                return t.intValue( isRevolve );
            }
        }
        return list[0].intValue( isRevolve );
    }

    private TYPE[] getBest( TURN turn, Set<TYPE> set ) {
        boolean hasJK = set.contains( TYPE.FF );
        List<Integer> powerlist = getGroupList( turn, hasJK );
        if ( !powerlist.isEmpty() ) {
            for ( Integer power : powerlist ) {
                TYPE[] t = getEquivalenceTypes( turn.getMyCards(), power.intValue(), -1 );
                if ( turn.getMyCards().list.length != t.length ) {
                    if ( t.length == 4 && !isRevolve ) {
                        return new TYPE[] { t[0], t[1] };
                    }
                    if ( t[0].intValue( isRevolve ) >= 13 ) {
                        break;
                    }
                }
                return t;
            }
        }
        return new TYPE[] { turn.getMyCards().list[0] };
    }

    private List<Integer> getGroupList( TURN turn, boolean hasJK ) {
        LinkedHashMap<Integer, Integer> group = new LinkedHashMap<Integer, Integer>();
        for ( TYPE t : turn.getMyCards().list ) {
            Integer count = group.get( Integer.valueOf( t.intValue( isRevolve ) ) );
            if ( count == null ) {
                count = Integer.valueOf( 1 );
            }
            else {
                count = Integer.valueOf( count.intValue() + 1 );
            }
            group.put( Integer.valueOf( t.intValue( isRevolve ) ), count );
        }
        List<Integer> powerlist = new ArrayList<Integer>();
        int offset = 1 + ( hasJK ? -1 : 0 );
        for ( Integer power : group.keySet() ) {
            if ( power.intValue() != TYPE.FF.intValue() ) {
                Integer count = group.get( power );
                if ( count.intValue() > offset ) {
                    powerlist.add( power );
                }
            }
        }
        return powerlist;
    }

    private Map<Integer, Integer> getGroupMap( TYPE[] cards, int num, boolean hasJK ) {
        LinkedHashMap<Integer, Integer> group = new LinkedHashMap<Integer, Integer>();
        for ( TYPE t : cards ) {
            Integer count = group.get( Integer.valueOf( t.intValue( isRevolve ) ) );
            if ( count == null ) {
                count = Integer.valueOf( 1 );
            }
            else {
                count = Integer.valueOf( count.intValue() + 1 );
            }
            group.put( t.intValue( isRevolve ), count );
        }
        Map<Integer, Integer> powermap = new LinkedHashMap<Integer, Integer>();
        for ( Integer power : group.keySet() ) {
            Integer count = group.get( power );
            if ( count.intValue() == num ) {
                if ( power.intValue() != TYPE.FF.intValue() ) {
                    powermap.put( power, count );
                }
            }
            else if ( hasJK && ( count.intValue() + 1 ) == num ) {
                if ( power.intValue() != TYPE.FF.intValue() ) {
                    powermap.put( power, count );
                }
            }
        }
        return powermap;
    }

    TYPE[] getEquivalenceTypes( Card c, int power, int size ) {
        List<TYPE> list = new ArrayList<TYPE>();
        for ( TYPE t : c.list ) {
            if ( power == t.intValue( isRevolve ) ) {
                list.add( t );
                if ( size == 1 ) {
                    break;
                }
            }
        }
        if ( list.size() < size && 0 < size ) {
            list.add( TYPE.FF );
        }
        return list.toArray( new TYPE[] {} );
    }

    private Command.PLAY select( TYPE card, String comment ) {
        return this.select( new TYPE[] { card }, comment );
    }

    private Command.PLAY select( TYPE[] card, String comment ) {
        try {
            return new Command.PLAY( new Card( card ), comment );
        }
        catch ( DFException e ) {
            e.printStackTrace();
            throw new RuntimeException( e );
        }
    }

    public static void main( String[] args ) {
        if ( args != null && args.length > 0 && !"".equals( args[0].trim() ) && args[0].indexOf( " " ) == -1 ) {
            try {
                new DF( args[0] );
            }
            catch ( Exception e ) {
                StackTraceElement[] stack = e.getStackTrace();
                StringBuilder sb = new StringBuilder();
                sb.append( args[0] ).append( "\r\n" );
                sb.append( e ).append( "\r\n" );
                for ( StackTraceElement el : stack ) {
                    sb.append( el.toString() ).append( "\r\n" );
                }
                System.err.println( sb.toString() );
            }
        }
        else {
            try {
                new DF( "nori090" );
            }
            catch ( Exception e ) {
                StackTraceElement[] stack = e.getStackTrace();
                StringBuilder sb = new StringBuilder();
                sb.append( "nori090" ).append( "\r\n" );
                sb.append( e ).append( "\r\n" );
                for ( StackTraceElement el : stack ) {
                    sb.append( el.toString() ).append( "\r\n" );
                }
                System.err.println( sb.toString() );
            }
        }
    }
}
