Skip to content

Commit

Permalink
Merge remote-tracking branch 'upstream/master' into liars_poker
Browse files Browse the repository at this point in the history
  • Loading branch information
ciamac committed Oct 19, 2023
2 parents a12a47d + fd09931 commit 41ac21a
Show file tree
Hide file tree
Showing 21 changed files with 481 additions and 136 deletions.
263 changes: 168 additions & 95 deletions open_spiel/games/chess/chess_board.cc

Large diffs are not rendered by default.

19 changes: 15 additions & 4 deletions open_spiel/games/chess/chess_board.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
#include <cstdint>
#include <functional>
#include <ostream>
#include <set>
#include <string>
#include <utility>
#include <vector>
Expand Down Expand Up @@ -279,9 +280,17 @@ class ChessBoard {
int32_t IrreversibleMoveCounter() const { return irreversible_move_counter_; }
int32_t Movenumber() const { return move_number_; }

bool CastlingRight(Color side, CastlingDirection direction) const;
absl::optional<Square> MaybeCastlingRookSquare(
Color side, CastlingDirection direction) const;

bool CastlingRight(Color color, CastlingDirection dir) const {
return MaybeCastlingRookSquare(color, dir).has_value();
}

void SetCastlingRight(Color side, CastlingDirection direction,
bool can_castle);
absl::optional<Square> maybe_rook_square);

Square FindRookForCastling(Color color, CastlingDirection dir) const;

// Find the location of any one piece of the given type, or kInvalidSquare.
Square find(const Piece& piece) const;
Expand Down Expand Up @@ -538,9 +547,11 @@ class ChessBoard {
// chess is a "half move" by white followed by a "half move" by black).
int32_t move_number_;

// Set to the square of the rook if castling is still possible in that
// direction, otherwise nullopt.
struct {
bool left_castle; // -x direction, AKA long castle
bool right_castle; // +x direction, AKA short castle
absl::optional<Square> left_castle; // -x direction, AKA long castle
absl::optional<Square> right_castle; // +x direction, AKA short castle
} castling_rights_[2];

uint64_t zobrist_hash_;
Expand Down
16 changes: 16 additions & 0 deletions open_spiel/games/chess/chess_common.h
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,22 @@ struct Square {

bool operator!=(const Square& other) const { return !(*this == other); }

// Required by std::set.
bool operator<(const Square& other) const {
if (x != other.x) {
return x < other.x;
} else {
return y < other.y;
}
}

std::string ToString() const {
std::string s;
s.push_back('a' + x);
s.push_back('1' + y);
return s;
}

int8_t x;
int8_t y;
};
Expand Down
57 changes: 50 additions & 7 deletions open_spiel/games/chess/chess_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,12 @@

#include "open_spiel/games/chess/chess.h"

#include <cstdint>
#include <memory>
#include <string>
#include <vector>

#include "open_spiel/abseil-cpp/absl/types/optional.h"
#include "open_spiel/games/chess/chess_board.h"
#include "open_spiel/spiel.h"
#include "open_spiel/spiel_utils.h"
Expand All @@ -28,13 +31,27 @@ namespace {

namespace testing = open_spiel::testing;

int CountNumLegalMoves(const ChessBoard& board) {
int num_legal_moves = 0;
board.GenerateLegalMoves([&num_legal_moves](const Move&) -> bool {
++num_legal_moves;
uint64_t Perft(const ChessBoard& board, int depth) {
std::vector<Move> legal_moves;
board.GenerateLegalMoves([&legal_moves](const Move& move) -> bool {
legal_moves.push_back(move);
return true;
});
return num_legal_moves;
if (depth == 1) {
return legal_moves.size();
} else {
uint64_t ret = 0;
for (const auto& move : legal_moves) {
ChessBoard board_copy = board;
board_copy.ApplyMove(move);
ret += Perft(board_copy, depth - 1);
}
return ret;
}
}

uint64_t Perft(const char* fen, int depth) {
return Perft(ChessBoard::BoardFromFEN(fen).value(), depth);
}

void CheckUndo(const char* fen, const char* move_san, const char* fen_after) {
Expand Down Expand Up @@ -64,8 +81,34 @@ void BasicChessTests() {
}

void MoveGenerationTests() {
ChessBoard start_pos = MakeDefaultBoard();
SPIEL_CHECK_EQ(CountNumLegalMoves(start_pos), 20);
// These perft positions and results are from here:
// https://www.chessprogramming.org/Perft_Results
// They are specifically designed to catch move generator bugs.
// Depth chosen for maximum a few seconds run time in debug build.
SPIEL_CHECK_EQ(Perft(MakeDefaultBoard(), 5), 4865609);
SPIEL_CHECK_EQ(
Perft("r3k2r/p1ppqpb1/bn2pnp1/3PN3/1p2P3/2N2Q1p/PPPBBPPP/R3K2R w KQkq -",
4),
4085603);
SPIEL_CHECK_EQ(Perft("8/2p5/3p4/KP5r/1R3p1k/8/4P1P1/8 w - -", 5), 674624);
SPIEL_CHECK_EQ(
Perft("r3k2r/Pppp1ppp/1b3nbN/nP6/BBP1P3/q4N2/Pp1P2PP/R2Q1RK1 w kq - 0 1",
4),
422333);
SPIEL_CHECK_EQ(
Perft("rnbq1k1r/pp1Pbppp/2p5/8/2B5/8/PPP1NnPP/RNBQK2R w KQ - 1 8", 4),
2103487);
SPIEL_CHECK_EQ(
Perft(
"r4rk1/1pp1qppp/p1np1n2/2b1p1B1/2B1P1b1/P1NP1N2/1PP1QPPP/R4RK1 w - -",
4),
3894594);

// Rook disambiguation:
// https://github.com/google-deepmind/open_spiel/issues/1125
SPIEL_CHECK_EQ(
Perft("4k1rr/1b1p3p/nn1p4/P3Np2/3P1bp1/6PP/P5R1/1B1K2N1 b k - 1 37", 1),
35);
}

void TerminalReturnTests() {
Expand Down
54 changes: 25 additions & 29 deletions open_spiel/python/algorithms/stackelberg_lp.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,9 @@
EC'06
"""

import cvxpy as cp
import numpy as np

from open_spiel.python.algorithms import lp_solver
from open_spiel.python.algorithms.projected_replicator_dynamics import _simplex_projection
from open_spiel.python.egt.utils import game_payoffs_array

Expand Down Expand Up @@ -51,39 +51,35 @@ def solve_stackelberg(game, is_first_leader=True):
follower_eq_strategy = None

for t in range(num_follower_strategies):
lp = lp_solver.LinearProgram(objective=lp_solver.OBJ_MAX)
for s in range(num_leader_strategies):
lp.add_or_reuse_variable("s_{}".format(s))
lp.set_obj_coeff("s_{}".format(s), leader_payoff[s, t])

p_s = cp.Variable(num_leader_strategies, nonneg=True)
constraints = [p_s <= 1, cp.sum(p_s) == 1]
for t_ in range(num_follower_strategies):
if t_ == t:
continue
lp.add_or_reuse_constraint("t_{}".format(t_), lp_solver.CONS_TYPE_GEQ)
for s in range(num_leader_strategies):
lp.set_cons_coeff("t_{}".format(t_), "s_{}".format(s),
follower_payoff[s, t] - follower_payoff[s, t_])
lp.set_cons_rhs("t_{}".format(t_), 0.0)
lp.add_or_reuse_constraint("sum_to_one", lp_solver.CONS_TYPE_EQ)
for s in range(num_leader_strategies):
lp.set_cons_coeff("sum_to_one", "s_{}".format(s), 1.0)
lp.set_cons_rhs("sum_to_one", 1.0)
try:
leader_strategy = np.array(lp.solve())
leader_strategy = _simplex_projection(
leader_strategy.reshape(-1)).reshape(-1, 1)
leader_value = leader_strategy.T.dot(leader_payoff)[0, t]
if leader_value > leader_eq_value:
leader_eq_strategy = leader_strategy
follower_eq_strategy = t
leader_eq_value = leader_value
follower_eq_value = leader_strategy.T.dot(follower_payoff)[0, t]
except: # pylint: disable=bare-except
constraints.append(
p_s @ follower_payoff[:, t_] <= p_s @ follower_payoff[:, t]
)
prob = cp.Problem(cp.Maximize(p_s @ leader_payoff[:, t]), constraints)
prob.solve()
p_s_value = p_s.value
if p_s_value is None:
continue
leader_strategy = _simplex_projection(p_s.value.reshape(-1)).reshape(-1, 1)
leader_value = leader_strategy.T.dot(leader_payoff)[0, t]
if leader_value > leader_eq_value:
leader_eq_strategy = leader_strategy
follower_eq_strategy = t
leader_eq_value = leader_value
follower_eq_value = leader_strategy.T.dot(follower_payoff)[0, t]

assert leader_eq_strategy is not None, p_mat
if is_first_leader:
return (leader_eq_strategy.reshape(-1), np.identity(
num_follower_strategies)[follower_eq_strategy],
leader_eq_value, follower_eq_value)
return (
leader_eq_strategy.reshape(-1),
np.identity(num_follower_strategies)[follower_eq_strategy],
leader_eq_value,
follower_eq_value,
)
else:
return (np.identity(num_follower_strategies)[follower_eq_strategy],
leader_eq_strategy.reshape(-1), follower_eq_value, leader_eq_value)
11 changes: 10 additions & 1 deletion open_spiel/python/algorithms/stackelberg_lp_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@
from open_spiel.python.egt.utils import game_payoffs_array
import pyspiel

# Numerical tolerance for tests.
EPS = 1e-6

# game instances based on Conitzer & Sandholm'06 paper
game0 = pyspiel.create_matrix_game([[2, 4], [1, 3]], [[1, 0], [0, 1]])
commit_strategy0 = np.array([0.5, 0.5])
Expand All @@ -32,12 +35,18 @@
commit_strategy1 = np.array([1 / 3, 2 / 3])
commit_value1 = 4 / 3

# a game with dominated strategy
game2 = pyspiel.create_matrix_game([[3, 9], [9, 1]], [[0, 0], [1, 8]])
commit_strategy2 = np.array([1.0, 0.0])
commit_value2 = 9.0


class StackelbergLPTest(parameterized.TestCase):

@parameterized.named_parameters(
("game0", game0, commit_strategy0, commit_value0),
("game1", game1, commit_strategy1, commit_value1),
("game2", game2, commit_strategy2, commit_value2),
)
def test_simple_games(self, game, commit_strategy, commit_value):
leader_eq_strategy, _, leader_eq_value, _ = solve_stackelberg(game)
Expand All @@ -53,7 +62,7 @@ def test_simple_games(self, game, commit_strategy, commit_value):
leader_nash_value = eq[0].reshape(1,
-1).dot(p_mat[0]).dot(eq[1].reshape(
-1, 1))
self.assertGreaterEqual(leader_eq_value, leader_nash_value)
self.assertGreaterEqual(leader_eq_value - leader_nash_value, -EPS)


if __name__ == "__main__":
Expand Down
14 changes: 14 additions & 0 deletions open_spiel/python/games/chat_games/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# Copyright 2019 DeepMind Technologies Limited
#
# 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.

14 changes: 14 additions & 0 deletions open_spiel/python/games/chat_games/configs/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# Copyright 2019 DeepMind Technologies Limited
#
# 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.

14 changes: 14 additions & 0 deletions open_spiel/python/games/chat_games/envs/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# Copyright 2019 DeepMind Technologies Limited
#
# 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.

14 changes: 14 additions & 0 deletions open_spiel/python/games/chat_games/envs/base_envs/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# Copyright 2019 DeepMind Technologies Limited
#
# 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.

Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# Copyright 2019 DeepMind Technologies Limited
#
# 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.

14 changes: 14 additions & 0 deletions open_spiel/python/games/chat_games/envs/observations/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# Copyright 2019 DeepMind Technologies Limited
#
# 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.

14 changes: 14 additions & 0 deletions open_spiel/python/games/chat_games/envs/payoffs/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# Copyright 2019 DeepMind Technologies Limited
#
# 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.

14 changes: 14 additions & 0 deletions open_spiel/python/games/chat_games/envs/scenarios/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# Copyright 2019 DeepMind Technologies Limited
#
# 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.

Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# Copyright 2019 DeepMind Technologies Limited
#
# 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.

Loading

0 comments on commit 41ac21a

Please sign in to comment.