Пример #1
0
 def test_add_subtract(self):
     d1 = strategy.MoveCountDistribution([0.75, 0.25])
     d2 = strategy.MoveCountDistribution([0.7, 0.2, 0.1])
     np.testing.assert_allclose((d1 + d2).dist, [1.45, 0.45, 0.1])
     np.testing.assert_allclose((d2 + d1).dist, [1.45, 0.45, 0.1])
     np.testing.assert_allclose((d1 - d2).dist, [0.05, 0.05, -0.1])
     np.testing.assert_allclose((d2 - d1).dist, [-0.05, -0.05, 0.1])
Пример #2
0
 def test_expected_value(self):
     np.testing.assert_approx_equal(
         strategy.MoveCountDistribution([0.75, 0.25]).expected_value(),
         0.25)
     np.testing.assert_approx_equal(
         strategy.MoveCountDistribution([0.0, 0.2, 0.8]).expected_value(),
         1.8)
     np.testing.assert_approx_equal(
         strategy.MoveCountDistribution([0.0, 0.0, 1.0]).expected_value(),
         2)
Пример #3
0
 def test_trim_low_prob(self):
     np.testing.assert_allclose(
         strategy.MoveCountDistribution([0.1, 0.2,
                                         0.3]).trim_low_prob(0.01).dist,
         [0.1, 0.2, 0.3])
     np.testing.assert_allclose(
         strategy.MoveCountDistribution([0.1, 0.2,
                                         0.3]).trim_low_prob(0.15).dist,
         [0.0, 0.2, 0.3])
     np.testing.assert_allclose(
         strategy.MoveCountDistribution([0.3, 0.2, 0.1,
                                         0.01]).trim_low_prob(0.15).dist,
         [0.3, 0.2])
Пример #4
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
Пример #5
0
def parse_gnubg_dump(config, gnubg_str):
    """Parse the output of gnubg's "bearoffdump".
    
    This parses the "Opponent" move distribution from thet "Bearing off" column

    Args:
      config: board.GameConfiguration
      gnubg_str: string to parse

    Returns
      board.Board, strategy.MoveCountDistribution
    """
    pos_id_str = None
    mcd = None
    parsing_mcd = False

    pos_id_re = re.compile(r'GNU Backgammon  Position ID: ([A-Za-z0-9+/]*)')
    start_mcd_re = re.compile(r'^Rolls\s+Player\s+Opponent')
    mcd_line_re = re.compile(r'^\s*\d+\s+[\d\.]+\s+([\d\.]+)')

    for line in gnubg_str.splitlines():
        if parsing_mcd:
            match = mcd_line_re.search(line)
            if match:
                value = float(match.group(1)) / 100.0
                if not mcd:
                    mcd = strategy.MoveCountDistribution([value])
                else:
                    mcd = mcd.append([value])
            else:
                parsing_mcd = False

        elif start_mcd_re.search(line):
            parsing_mcd = True

        else:
            match = pos_id_re.search(line)
            if match:
                pos_id_str = match.group(1)

    if not pos_id_str:
        raise ValueError('Never found position id line')
    if not mcd:
        raise ValueError('Never found move distribution')

    return (board.Board.from_id(config,
                                gnubg_id_str_to_board_id(config,
                                                         pos_id_str)), mcd)
Пример #6
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)
Пример #7
0
 def test_append(self):
     d = strategy.MoveCountDistribution([0.1, 0.2])
     np.testing.assert_allclose(
         d.append([0.3, 0.4]).dist, [0.1, 0.2, 0.3, 0.4])
Пример #8
0
 def test_is_normalized(self):
     self.assertTrue(
         strategy.MoveCountDistribution([0.75, 0.25]).is_normalized())
     self.assertFalse(
         strategy.MoveCountDistribution([0.1, 0.2]).is_normalized())
Пример #9
0
 def test_increase_counts(self):
     d = strategy.MoveCountDistribution([0.75, 0.25])
     np.testing.assert_allclose(
         d.increase_counts(2).dist, [0, 0, 0.75, 0.25])
Пример #10
0
 def test_multiply_divide(self):
     d = strategy.MoveCountDistribution([0.75, 0.25])
     np.testing.assert_allclose((d * 2).dist, [1.5, .5])
     np.testing.assert_allclose((d / 5).dist, [.15, .05])
Пример #11
0
 def test_init(self):
     d = strategy.MoveCountDistribution()
     np.testing.assert_allclose(d.dist, [0])
     with self.assertRaises(ValueError):
         strategy.MoveCountDistribution([[0.1], [0.2]])