示例#1
0
 def test_generate_moves_one_valid(self):
     config = board.GameConfiguration(6, 2)
     b = board.Board(config, [1, 2, 3])
     got = list(b.generate_moves(board.Roll(dice=[5, 4], prob=0)))
     self.assertEqual(2, len(got))
     self.assertIn([board.Move(2, 5), board.Move(2, 4)], got)
     self.assertIn([board.Move(2, 4), board.Move(2, 5)], got)
示例#2
0
 def test_initialize_3marker_2spots(self):
     config = board.GameConfiguration(3, 2)
     self.assertEqual(config.num_valid_boards, 10)
     # 5 total bits 00111 is the min
     self.assertEqual(config.min_board_id, 7)
     # 5 total bits 11100 is the max, adding 1 for exclusive
     self.assertEqual(config.max_board_id, 28 + 1)
示例#3
0
 def test_is_valid_counts(self, num_markers, num_spots):
     config = board.GameConfiguration(num_markers, num_spots)
     count_valid = 0
     for idx in range(config.min_board_id, config.max_board_id):
         if config.is_valid_id(idx):
             count_valid += 1
     self.assertEqual(config.num_valid_boards, count_valid)
示例#4
0
 def test_id_round_trip(self, num_markers, num_spots):
     config = board.GameConfiguration(num_markers, num_spots)
     for idx in range(config.min_board_id, config.max_board_id):
         if not config.is_valid_id(idx):
             continue
         b = board.Board.from_id(config, idx)
         self.assertEqual(b.get_id(), idx)
示例#5
0
 def test_pretty_print(self):
     config = board.GameConfiguration(6, 2)
     # Board state is 1 off, 2 on 1 spot, 3 on 2 spot
     # 11101101
     b = board.Board.from_id(config, 0xED)
     self.assertEqual(b.pretty_string(),
                      "0 1 o   \n" + "1 2 oo  \n" + "2 3 ooo \n")
示例#6
0
 def test_next_valid_board(self, num_markers, num_spots):
     config = board.GameConfiguration(num_markers, num_spots)
     b = board.Board.from_id(config, config.min_board_id)
     id_gen = config.generate_valid_ids()
     next(id_gen)
     while True:
         msg = "old b: %s; " % str(b)
         try:
             next_b = b.next_valid_board()
             msg += "next b: %s; " % str(next_b)
         except StopIteration:
             try:
                 next_idx = next(id_gen)
                 self.fail(
                     "Generator not finished with next_valid_board was %s" %
                     msg)
             except StopIteration:
                 break
         try:
             next_idx = next(id_gen)
             msg += "next idx: %d; " % next_idx
         except StopIteration:
             self.fail("Generator ran out before board; %s" % msg)
         self.assertEqual(next_b.get_id(), next_idx, msg)
         b = next_b
示例#7
0
 def test_to_string(self):
     config = board.GameConfiguration(15, 6)
     self.assertEqual(
         'AQAAAAAAAAAAAA',
         gnubg_interface.board_id_to_gnubg_id_str(
             config,
             board.Board(config, [14, 1, 0, 0, 0, 0, 0]).get_id()))
示例#8
0
    def test_e2e_6_3(self):
        config = board.GameConfiguration(6, 3)
        store = strategy.DistributionStore(config)
        store.compute()

        def test_move_count_distribution(board_init, expected_move_count):
            print("board_init: %s" % board_init)
            b = board.Board(config, board_init)
            np.testing.assert_allclose(store.distribution_map[b.get_id()].dist,
                                       expected_move_count)

        # This is an end to end test. We'll pull a few specific cases
        # that are relatively easy to understand.

        # The end game state
        test_move_count_distribution([6, 0, 0, 0], [1])
        # Only two pieces left, has to finish on one roll
        test_move_count_distribution([4, 2, 0, 0], [0, 1])
        # Only three pieces left. If doubles, finish in 1 move otherwise, 2.
        test_move_count_distribution([3, 3, 0, 0], [0, 1 / 6, 5 / 6])
        # Only two pieces left but on 2nd spot. Any of the 1-2, 1-3,
        # 1-4, 1-5, 1-6 rolls mean you will finish in 2 instead of 1.
        test_move_count_distribution([4, 0, 2, 0], [0, 26 / 36, 10 / 36])
        # Only two pieces left but on 1st and 3rd spot. Any roll
        # except 1-2 and you go out in 1 roll
        test_move_count_distribution([4, 1, 0, 1], [0, 34 / 36, 2 / 36])
示例#9
0
 def test_generate_moves_unfinished(self):
     # This is where you don't have to use all the moves to finish
     config = board.GameConfiguration(2, 2)
     b = board.Board(config, [1, 0, 1])
     got = list(b.generate_moves(board.Roll(dice=[5, 4], prob=0)))
     self.assertEqual(2, len(got))
     self.assertIn([board.Move(2, 4)], got)
     self.assertIn([board.Move(2, 5)], got)
示例#10
0
 def test_generate_moves_valid_with_holes(self):
     config = board.GameConfiguration(3, 4)
     # Making sure we don't generate the move that is the 1 spot
     # going 4 spaces.`
     b = board.Board(config, [0, 1, 0, 2, 0])
     got = list(b.generate_moves(board.Roll(dice=[4, 4], prob=0)))
     self.assertEqual(1, len(got))
     self.assertIn([board.Move(3, 4), board.Move(3, 4)], got)
示例#11
0
 def test_spot_counts_init_errors(self):
     config = board.GameConfiguration(6, 2)
     with self.assertRaises(ValueError):
         b = board.Board(config, [5, 1])
     with self.assertRaises(ValueError):
         b = board.Board(config, [3, 1, 1, 1])
     with self.assertRaises(ValueError):
         b = board.Board(config, [1, 1, 1])
示例#12
0
 def test_generate_moves_opposite_order(self):
     # This is the case where you have to try applying the dice in the
     # opposite order in order to get a different board as outcome.
     config = board.GameConfiguration(2, 4)
     b = board.Board(config, [0, 0, 1, 0, 1])
     got = list(b.generate_moves(board.Roll(dice=[4, 3], prob=0)))
     self.assertEqual(2, len(got))
     self.assertIn([board.Move(4, 3), board.Move(2, 4)], got)
     self.assertIn([board.Move(4, 4), board.Move(2, 3)], got)
示例#13
0
    def test_apply_move(self):
        config = board.GameConfiguration(6, 2)
        b = board.Board(config, [1, 2, 3])

        self.assertEqual(b.apply_move(board.Move(2, 6)),
                         board.Board(config, [2, 2, 2]))
        self.assertEqual(b.apply_move(board.Move(2, 1)),
                         board.Board(config, [1, 3, 2]))
        self.assertEqual(b.apply_move(board.Move(1, 1)),
                         board.Board(config, [2, 1, 3]))
示例#14
0
 def test_round_trip(self):
     config = board.GameConfiguration(15, 6)
     for board_id in config.generate_valid_ids():
         gnubg_str = gnubg_interface.board_id_to_gnubg_id_str(
             config, board_id)
         new_board_id = gnubg_interface.gnubg_id_str_to_board_id(
             config, gnubg_str)
         self.assertEqual(board_id,
                          new_board_id,
                          msg='gnbg_str={}'.format(gnubg_str))
示例#15
0
 def test_generate_moves_valid(self, roll):
     # For a random board position, we'll just check that all
     # generated moves are valid.
     config = board.GameConfiguration(6, 4)
     b = board.Board(config, [0, 2, 3, 0, 1])
     for moves in b.generate_moves(roll):
         try:
             b.apply_moves(moves)
         except Exception as e:
             print("%s with moves %s" % (roll, moves))
             raise e
示例#16
0
    def test_pretty_print_with_moves(self):
        config = board.GameConfiguration(6, 4)
        b = board.Board(config, [2, 1, 1, 1, 1])

        m1 = board.Move(spot=3, count=2)
        m2 = board.Move(spot=3, count=3)
        m3 = board.Move(spot=3, count=6)
        m4 = board.Move(spot=4, count=1)
        self.assertEqual(
            b.pretty_string([m1, m2, m3,
                             m4]), "0 2 oo   x +   \n" + "1 1 o  x | |   \n" +
            "2 1 o  | | |   \n" + "3 1 o  2 3 6 x \n" + "4 1 o        1 \n")
示例#17
0
    def test_apply_move_errors(self):
        config = board.GameConfiguration(6, 2)

        b = board.Board(config, [1, 2, 3])
        with self.assertRaisesRegex(ValueError, "Invalid spot"):
            b.apply_move(board.Move(0, 6))
        b = board.Board(config, [3, 0, 3])
        with self.assertRaisesRegex(ValueError, "No marker"):
            b.apply_move(board.Move(1, 1))
        b = board.Board(config, [1, 2, 3])
        with self.assertRaisesRegex(ValueError, "Overflow count"):
            b.apply_move(board.Move(1, 6))
示例#18
0
def create_distribution_store_from_gnubg(gnubg_dir):
    """Creates a database in our format from the gnubg database.

    This uses a really dumb and inefficient strategy of calling
    'bearoffdump' many times as separate processes. But it allows us
    to not tweak the gnubg code and not have to deal with the vagaries
    of their format (just some annoying text parsing)

    Args:
      gnubg_dir: source directory of gnubg with everyting compiled 

    Returns:
      strategy.DistributionStore

    """
    progress_interval = 500

    config = board.GameConfiguration(15, 6)
    store = strategy.DistributionStore(config)

    start_time = time.time()

    # gnubg uses a 1 based index as an argument to
    # bearoffdump. However, it doesn't have the end state as a valid
    # index so we add that manually.
    store.distribution_map[config.min_board_id] = (
        strategy.MoveCountDistribution([1]))
    for idx in range(1, config.num_valid_boards):
        completed_process = subprocess.run([
            os.path.join(gnubg_dir, 'bearoffdump'),
            os.path.join(gnubg_dir, 'gnubg_os0.bd'), '-n',
            str(idx)
        ],
                                           universal_newlines=True,
                                           stdout=subprocess.PIPE,
                                           check=True)

        try:
            b, mcd = parse_gnubg_dump(config, completed_process.stdout)
        except ValueError as err:
            raise ValueError('For gnubg index {}: {}'.format(idx, err))
        store.distribution_map[b.get_id()] = mcd

        if idx % progress_interval == 0:
            frac_complete = idx / config.num_valid_boards
            this_time = time.time()
            print("%d/%d %.1f%%, %fs elapsed, %fs estimated total" %
                  (idx, config.num_valid_boards, frac_complete * 100,
                   this_time - start_time,
                   (this_time - start_time) / frac_complete),
                  flush=True)

    return store
示例#19
0
 def test_generate_moves_four_dice(self):
     config = board.GameConfiguration(9, 4)
     b = board.Board(config, [0, 0, 3, 3, 3])
     got = list(b.generate_moves(board.Roll(dice=[2, 2, 2, 2], prob=0)))
     self.assertEqual(78, len(got))
     # Since this generates an annoying large number of moves,
     # we're not going to test all of them, just a couple.
     self.assertIn([
         board.Move(3, 2),
         board.Move(2, 2),
         board.Move(2, 2),
         board.Move(2, 2)
     ], got)
     self.assertIn([
         board.Move(3, 2),
         board.Move(3, 2),
         board.Move(3, 2),
         board.Move(2, 2)
     ], got)
示例#20
0
    def test_round_trip_save_load(self):
        config = board.GameConfiguration(3, 2)
        store = strategy.DistributionStore(config)
        store.compute()

        with tempfile.TemporaryFile() as tmp:
            store.save_hdf5(tmp)

            tmp.seek(0)

            loaded_store = strategy.DistributionStore.load_hdf5(tmp)

            self.assertEqual(store.config.num_markers,
                             loaded_store.config.num_markers)
            self.assertEqual(store.config.num_spots,
                             loaded_store.config.num_spots)
            self.assertEqual(len(store.distribution_map),
                             len(loaded_store.distribution_map))
            for board_id, mcd in store.distribution_map.items():
                np.testing.assert_allclose(
                    mcd.dist, loaded_store.distribution_map[board_id].dist)
示例#21
0
    def test_next_valid_id(self, num_markers, num_spots):
        config = board.GameConfiguration(num_markers, num_spots)
        board_id = config.min_board_id
        while True:
            try:
                next_board_id = config.next_valid_id(board_id)
            except StopIteration:
                next_board_id = -1
            expected_board_id = board_id + 1
            while not (config.is_valid_id(expected_board_id)):
                expected_board_id += 1
                if expected_board_id >= config.max_board_id:
                    expected_board_id = -1
                    break
            self.assertEqual(next_board_id, expected_board_id,
                             "previous board idx: %d" % board_id)

            if next_board_id == -1:
                break

            board_id = next_board_id
示例#22
0
 def test_from_string(self):
     config = board.GameConfiguration(15, 6)
     # index 1
     self.assertEqual(
         board.Board(config, [14, 1, 0, 0, 0, 0, 0]).get_id(),
         gnubg_interface.gnubg_id_str_to_board_id(config, 'AQAAAAAAAAAAAA'))
     # index 2
     self.assertEqual(
         board.Board(config, [14, 0, 1, 0, 0, 0, 0]).get_id(),
         gnubg_interface.gnubg_id_str_to_board_id(config, 'AgAAAAAAAAAAAA'))
     # index 99
     self.assertEqual(
         board.Board(config, [11, 2, 0, 0, 2, 0, 0]).get_id(),
         gnubg_interface.gnubg_id_str_to_board_id(config, 'YwAAAAAAAAAAAA'))
     # index 33333
     self.assertEqual(
         board.Board(config, [1, 1, 11, 0, 0, 1, 1]).get_id(),
         gnubg_interface.gnubg_id_str_to_board_id(config, '/R8FAAAAAAAAAA'))
     # index 50000 (everything is on the board)
     self.assertEqual(
         board.Board(config, [0, 1, 0, 3, 8, 3, 0]).get_id(),
         gnubg_interface.gnubg_id_str_to_board_id(config, 'uX8HAAAAAAAAAA'))
示例#23
0
 def test_total_pips(self):
     config = board.GameConfiguration(6, 3)
     self.assertEqual(0, board.Board(config, [6, 0, 0, 0]).total_pips())
     self.assertEqual(5, board.Board(config, [1, 5, 0, 0]).total_pips())
     self.assertEqual(14, board.Board(config, [0, 1, 2, 3]).total_pips())
示例#24
0
    def testParse(self):
        input = """
Bearoff database: gnubg_os0.bd
Position number : 30

Information about database:

 * On disk 1-sided bearoff database evaluator
   - generated by GNU Backgammon
   - up to 15 chequers on 6 points (54264 positions) per player
   - database includes gammon distributions



Dump of position#: 30

 GNU Backgammon  Position ID: EwAAAAAAAAAAAA
 +13-14-15-16-17-18------19-20-21-22-23-24-+
 |                  |   |          O     O | OOO
 |                  |   |                O | OOO
 |                  |   |                  | OO
 |                  |   |                  | OO
 |                  |   |                  | OO
v|                  |BAR|                  |
 |                  |   |                  | XXX
 |                  |   |                  | XXX
 |                  |   |                  | XXX
 |                  |   |                  | XXX
 |                  |   |                  | XXX
 +12-11-10--9--8--7-------6--5--4--3--2--1-+

             Player       Opponent
Position            0            30

Bearing off                             Bearing at least one chequer off
Rolls   Player  Opponent        Player  Opponent
    0   100.000   0.000         100.000 100.000
    1     0.000  13.889           0.000   0.000
    2     0.000  86.111
Average rolls
Bearing off                             Saving gammon
        Player  Opponent        Player  Opponent
Mean      0.000   1.861           0.000   0.000
Std dev   0.000   0.346           0.000   0.000

Effective pip count:
        Player  Opponent
EPC       0.000  15.199
Wastage   0.000  10.199

EPC = 8.167 * Average rolls
Wastage = EPC - pips
        """
        config = board.GameConfiguration(15, 6)
        b, mcd = gnubg_interface.parse_gnubg_dump(config, input)

        expected_b = board.Board(config, [12, 2, 0, 1, 0, 0, 0])
        self.assertEqual(b,
                         expected_b,
                         msg='expected={}, got={}'.format(expected_b, b))

        expected_mcd = strategy.MoveCountDistribution([0.0, .13889, .86111])
        np.testing.assert_allclose(expected_mcd.dist, mcd.dist, atol=1e-5)
示例#25
0
 def test_generate_moves_second_dependent(self):
     config = board.GameConfiguration(2, 4)
     b = board.Board(config, [0, 0, 1, 0, 1])
     got = list(b.generate_moves(board.Roll(dice=[4, 4], prob=0)))
     self.assertEqual(1, len(got))
     self.assertIn([board.Move(4, 4), board.Move(2, 4)], got)
示例#26
0
# 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

#     https://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.

import argparse

import board
import strategy

parser = argparse.ArgumentParser()
parser.add_argument("num_markers")
parser.add_argument("num_spots")
args = parser.parse_args()
num_markers = int(args.num_markers)
num_spots = int(args.num_spots)

config = board.GameConfiguration(num_markers, num_spots)
store = strategy.DistributionStore(config)
store.compute()
fn = "data/bgend_store_%d_%d.hdf5" % (num_markers, num_spots)
store.save_hdf5(fn)
示例#27
0
 def test_generator(self, num_markers, num_spots):
     config = board.GameConfiguration(num_markers, num_spots)
     self.assertEqual(config.num_valid_boards,
                      sum(1 for _ in config.generate_valid_ids()))
示例#28
0
 def test_spot_counts_init(self):
     config = board.GameConfiguration(6, 2)
     b = board.Board(config, [1, 2, 3])
     self.assertListEqual(list(b.spot_counts), [1, 2, 3])
示例#29
0
 def test_id_init(self):
     config = board.GameConfiguration(6, 2)
     # Board state is 1 off, 2 on 1 spot, 3 on 2 spot
     # 11101101
     b = board.Board.from_id(config, 0xED)
     self.assertListEqual(list(b.spot_counts), [1, 2, 3])