edu.oregonstate.eecs.mcplan.domains.spbj.SpBjSimulator.java Source code

Java tutorial

Introduction

Here is the source code for edu.oregonstate.eecs.mcplan.domains.spbj.SpBjSimulator.java

Source

/* LICENSE
Copyright (c) 2013-2016, Jesse Hostetler (jessehostetler@gmail.com)
All rights reserved.
    
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
    
1. Redistributions of source code must retain the above copyright notice,
   this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
   this list of conditions and the following disclaimer in the documentation
   and/or other materials provided with the distribution.
    
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

/**
 * 
 */
package edu.oregonstate.eecs.mcplan.domains.spbj;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Deque;

import org.apache.commons.math3.random.MersenneTwister;
import org.apache.commons.math3.random.RandomGenerator;

import edu.oregonstate.eecs.mcplan.JointAction;
import edu.oregonstate.eecs.mcplan.domains.cards.Card;
import edu.oregonstate.eecs.mcplan.domains.cards.Deck;
import edu.oregonstate.eecs.mcplan.domains.cards.StackedDeck;
import edu.oregonstate.eecs.mcplan.sim.UndoSimulator;
import edu.oregonstate.eecs.mcplan.util.Fn;

/**
 * Simulator for "Spanish 21". Rules for Spanish 21 are somewhat variable. We
 * use the following variant rules:
 * 
 * - Dealer stands on soft 17 - this is not standard when re-doubling is allowed
 * - Dealer does not immediately reveal natural Blackjack - nonstandard
 * - Player does not immediately reveal natural Blackjack - nonstandard
 * - Player may double up to 3 times
 * - Player may double after splitting
 * - Player may re-split up to a total of 4 hands
 * 
 * The main alteration is that gameplay rules that cause the game to end
 * before any decisions are made have been removed. We use S17 merely because
 * it eliminates a special case in the code.
 * 
 * The nonstandard aspects of the rules should decrease the house advantage.
 * Specifically, being able to draw to beat the dealer's Blackjack favors
 * the player, and S17 is better for the player than H17 (usually if
 * re-doubling is allowed then H17 is the rule).
 */
public class SpBjSimulator implements UndoSimulator<SpBjState, SpBjAction> {
    private final SpBjState s;
    //   private final double r;

    private final Deque<JointAction<SpBjAction>> action_history = new ArrayDeque<JointAction<SpBjAction>>();

    public SpBjSimulator(final SpBjState s) {
        this.s = s;
        //      r = 0;
    }

    @Override
    public SpBjState state() {
        return s;
    }

    @Override
    public void takeAction(final JointAction<SpBjAction> a) {
        for (final SpBjAction ai : a) {
            ai.doAction(s);
        }
        action_history.push(a);

        //      if( isTerminalState() ) {
        //         completeDealerHand();
        //      }
    }

    @Override
    public void untakeLastAction() {
        //      if( isTerminalState() ) {
        //         uncompleteDealerHand();
        //      }

        final JointAction<SpBjAction> a = action_history.pop();
        for (final SpBjAction ai : a) {
            ai.undoAction(s);
        }
    }

    @Override
    public long depth() {
        return action_history.size();
    }

    @Override
    public long t() {
        return action_history.size();
    }

    @Override
    public int nagents() {
        return 1;
    }

    @Override
    public int[] turn() {
        if (s.isTerminal()) {
            return new int[] { 0 };
        } else {
            return new int[] {};
        }
    }

    //   private void completeDealerHand()
    //   {
    //      final ArrayList<Card> hand = s.dealerHand();
    //      int[] dv = null;
    //      while( true ) {
    //         if( hand.size() < 2 ) {
    //            hand.add( s.deck().deal() );
    //         }
    //         else {
    //            dv = SpBjHand.handValue( hand );
    //            // Dealer stands on soft 17
    //            if( dv[0] <= SpBjHand.dealer_threshold ) {
    //               hand.add( s.deck().deal() );
    //            }
    //            else {
    //               break;
    //            }
    //         }
    //      }
    //
    //      final ArrayList<int[]> pv = s.player_hand.values();
    //      assert( r == 0 );
    //
    //      if( s.player_hand.Nhands == 1
    //            && pv.get( 0 )[0] == SpBjHand.max_score
    //            && s.player_hand.hands.get( 0 ).size() == 2 ) {
    //         // Natural Blackjack always wins and pays 3:2
    //         r += 3*s.player_hand.bets[0] / 2;
    //         return;
    //      }
    //
    //      for( int i = 0; i < pv.size(); ++i ) {
    //         final int[] pvi = pv.get( i );
    //         // Player bust
    //         if( pvi[0] > SpBjHand.max_score ) {
    //            r += -s.player_hand.bets[i];
    //         }
    //         // Player has 21
    //         else if( pvi[0] == SpBjHand.max_score ) {
    //            // Player didn't double -> eligible for bonuses
    //            if( s.player_hand.bets[i] == SpBjHand.min_bet ) {
    //               final ArrayList<Card> h = s.player_hand.hands.get( i );
    //               if( h.size() == 5 ) { // 5-card 21 pays 3:2
    //                  r += 3*s.player_hand.bets[i] / 2;
    //                  continue;
    //               }
    //               else if( h.size() == 6 ) { // 6-card 21 pays 2:1
    //                  r += 2*s.player_hand.bets[i];
    //                  continue;
    //               }
    //               else if( h.size() >= 7 ) { // 7-or-more-card 21 pays 3:1
    //                  r += 3*s.player_hand.bets[i];
    //                  continue;
    //               }
    //               else if( h.size() == 3 ) {
    //                  // Check for 678 / 777 bonus
    //                  final ArrayList<Card> sorted = Fn.copy( h );
    //                  Collections.sort( sorted, Card.TheAceHighRankComparator );
    //                  final Card c1 = sorted.get( 1 );
    //                  if( c1.rank == Rank.R_7 ) {
    //                     final Card c0 = sorted.get( 0 );
    //                     final Card c2 = sorted.get( 2 );
    //                     if( (c0.rank == Rank.R_6 && c2.rank == Rank.R_8)
    //                           || (c0.rank == Rank.R_7 && c2.rank == Rank.R_7) ) {
    //                        if( c0.suit == c1.suit && c1.suit == c2.suit ) {
    //                           // Suited
    //                           if( c0.suit == Suit.S_s ) {
    //                              // Spades pay 3:1
    //                              r += 3*s.player_hand.bets[i];
    //                              continue;
    //                           }
    //                           else {
    //                              // Non-spades pay 2:1
    //                              r += 2*s.player_hand.bets[i];
    //                              continue;
    //                           }
    //                        }
    //                        else {
    //                           // Not suited pays 3:2
    //                           r += 3*s.player_hand.bets[i] / 2;
    //                           continue;
    //                        }
    //                     }
    //                  }
    //               }
    //            }
    //
    //            // No bonuses, but player 21 always wins
    //            r += s.player_hand.bets[i];
    //         }
    //         // Dealer bust or player beats dealer
    //         else if( dv[0] > SpBjHand.max_score || pvi[0] > dv[0] ) {
    //            r += s.player_hand.bets[i];
    //         }
    //         // Dealer beats player
    //         else if( pvi[0] < dv[0] ) {
    //            r += -s.player_hand.bets[i];
    //         }
    //         else {
    //            assert( pvi[0] == dv[0] );
    //            // Ties are pushes
    //         }
    //      }
    //   }

    //   private void uncompleteDealerHand()
    //   {
    //      final ArrayList<Card> hand = s.dealerHand();
    //      while( hand.size() > 1 ) {
    //         s.deck().undeal( hand.remove( hand.size() - 1 ) );
    //      }
    //      r = 0;
    //   }

    @Override
    public double[] reward() {
        return new double[] { s.r };
    }

    @Override
    public boolean isTerminalState() {
        return s.isTerminal();
    }

    @Override
    public long horizon() {
        return Long.MAX_VALUE;
    }

    @Override
    public String detailString() {
        return "spanish-blackjack";
    }

    // -----------------------------------------------------------------------

    public static void main(final String[] argv) throws IOException {
        final int seed = 43;
        final RandomGenerator rng = new MersenneTwister(seed);
        //      final Deck deck = new InfiniteSpanishDeck( rng );

        // TODO: Debugging code
        final Deque<Card> stacked = new ArrayDeque<Card>();
        for (int i = 0; i < 20; ++i) {
            stacked.push(Card.C_2c);
            stacked.push(Card.C_2d);
            stacked.push(Card.C_2h);
            stacked.push(Card.C_2s);
        }
        final Deck deck = new StackedDeck(stacked);

        //      final ArrayList<ArrayList<Card>> test_hands = new ArrayList<ArrayList<Card>>();
        //      test_hands.add( new ArrayList<Card>( Arrays.asList( Card.C_Kc, Card.C_9h, Card.C_2c ) ) );
        //      test_hands.add( new ArrayList<Card>( Arrays.asList( Card.C_6c, Card.C_7h, Card.C_8c ) ) );
        //      test_hands.add( new ArrayList<Card>( Arrays.asList( Card.C_6c, Card.C_7c, Card.C_8c ) ) );
        //      test_hands.add( new ArrayList<Card>( Arrays.asList( Card.C_6s, Card.C_7s, Card.C_8s ) ) );
        //      test_hands.add( new ArrayList<Card>( Arrays.asList( Card.C_7c, Card.C_7h, Card.C_7c ) ) );
        //      test_hands.add( new ArrayList<Card>( Arrays.asList( Card.C_7c, Card.C_7c, Card.C_7c ) ) );
        //      test_hands.add( new ArrayList<Card>( Arrays.asList( Card.C_7s, Card.C_7s, Card.C_7s ) ) );
        //      test_hands.add( new ArrayList<Card>( Arrays.asList( Card.C_3c, Card.C_3c, Card.C_3c, Card.C_3c, Card.C_9c ) ) );
        //      test_hands.add( new ArrayList<Card>( Arrays.asList( Card.C_3c, Card.C_3c, Card.C_3c, Card.C_3c, Card.C_3c, Card.C_6c ) ) );
        //      test_hands.add( new ArrayList<Card>( Arrays.asList( Card.C_3c, Card.C_3c, Card.C_3c, Card.C_3c, Card.C_3c, Card.C_3c, Card.C_3c ) ) );
        //      test_hands.add( new ArrayList<Card>( Arrays.asList( Card.C_3c, Card.C_3c, Card.C_3c, Card.C_3c, Card.C_3c, Card.C_3c, Card.C_2c, Card.C_Ac ) ) );
        //
        //      final ArrayList<ArrayList<Card>> dealer_test_hands = new ArrayList<ArrayList<Card>>();
        //      dealer_test_hands.add( new ArrayList<Card>( Arrays.asList( Card.C_Kc, Card.C_Kc ) ) );
        //      dealer_test_hands.add( new ArrayList<Card>( Arrays.asList( Card.C_Kc, Card.C_Ac ) ) );
        //
        //      for( final ArrayList<Card> dealer_cards : dealer_test_hands ) {
        //         for( final ArrayList<Card> cards : test_hands ) {
        //            final SpBjState s = new SpBjState( deck );
        //            s.init();
        //            s.dealer_hand.clear();
        //            s.dealer_hand.addAll( dealer_cards );
        //            s.player_hand.hands.set( 0, cards );
        //            final SpBjSimulator sim = new SpBjSimulator( s );
        //            sim.takeAction( new JointAction<SpBjAction>(
        //               new SpBjAction( new SpBjActionCategory[] { SpBjActionCategory.Pass } ) ) );
        //
        //            System.out.print( "Hand: " );
        //            System.out.print( sim.state().player_hand );
        //            System.out.print( " (" );
        //            final ArrayList<int[]> values = sim.state().player_hand.values();
        //            for( int i = 0; i < values.size(); ++i ) {
        //               if( i > 0 ) {
        //                  System.out.print( ", " );
        //               }
        //               System.out.print( Arrays.toString( values.get( i ) ) );
        //            }
        //            System.out.println( ")" );
        //
        //            System.out.print( "Reward: " );
        //            System.out.println( Arrays.toString( sim.reward() ) );
        //            System.out.print( "Dealer hand: " );
        //            System.out.print( sim.state().dealerHand().toString() );
        //            System.out.print( " (" );
        //            System.out.print( SpBjHand.handValue( sim.state().dealerHand() )[0] );
        //            System.out.println( ")" );
        //            System.out.println( "----------------------------------------" );
        //         }
        //      }

        while (true) {
            final SpBjState s = new SpBjState(deck);
            s.init();
            final SpBjSimulator sim = new SpBjSimulator(s);

            final BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
            while (!s.isTerminal()) {
                System.out.print("Dealer showing: ");
                System.out.println(sim.state().dealerUpcard());

                System.out.print("Hand: ");
                System.out.print(sim.state().player_hand);
                System.out.print(" (");
                final ArrayList<int[]> values = sim.state().player_hand.values();
                for (int i = 0; i < values.size(); ++i) {
                    if (i > 0) {
                        System.out.print(", ");
                    }
                    System.out.print(Arrays.toString(values.get(i)));
                }
                System.out.println(")");

                final SpBjActionGenerator actions = new SpBjActionGenerator();
                actions.setState(sim.state(), 0);
                for (final SpBjAction a : Fn.in(actions)) {
                    System.out.println(a);
                }

                final String cmd = reader.readLine();
                assert (cmd.length() == sim.state().player_hand.Nhands);
                final SpBjActionCategory[] cat = new SpBjActionCategory[cmd.length()];
                for (int i = 0; i < cmd.length(); ++i) {
                    final char c = cmd.charAt(i);
                    if ('h' == c) {
                        cat[i] = SpBjActionCategory.Hit;
                    } else if ('p' == c) {
                        cat[i] = SpBjActionCategory.Pass;
                    } else if ('d' == c) {
                        cat[i] = SpBjActionCategory.Double;
                    } else if ('s' == c) {
                        cat[i] = SpBjActionCategory.Split;
                    }
                }
                sim.takeAction(new JointAction<SpBjAction>(new SpBjAction(cat)));
            }

            System.out.print("Hand: ");
            System.out.print(sim.state().player_hand);
            System.out.print(" (");
            final ArrayList<int[]> values = sim.state().player_hand.values();
            for (int i = 0; i < values.size(); ++i) {
                if (i > 0) {
                    System.out.print(", ");
                }
                System.out.print(Arrays.toString(values.get(i)));
            }
            System.out.println(")");

            System.out.print("Reward: ");
            System.out.println(Arrays.toString(sim.reward()));
            System.out.print("Dealer hand: ");
            System.out.print(sim.state().dealerHand().toString());
            System.out.print(" (");
            System.out.print(SpBjHand.handValue(sim.state().dealerHand())[0]);
            System.out.println(")");
            System.out.println("----------------------------------------");
        }
    }
}