Skip to content

Commit

Permalink
Merge pull request #511 from BedirT:master
Browse files Browse the repository at this point in the history
PiperOrigin-RevId: 359498995
Change-Id: Ie21883f49ac6c43fad480bcb970fdca76fed9ff8
  • Loading branch information
open_spiel@google.com authored and open_spiel@google.com committed Feb 25, 2021
2 parents 99d9ac7 + 6b7ce5e commit 306a32f
Show file tree
Hide file tree
Showing 10 changed files with 1,414 additions and 20 deletions.
6 changes: 6 additions & 0 deletions open_spiel/games/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ set(GAME_SOURCES
coordinated_mp.h
cursor_go.cc
cursor_go.h
dark_hex.cc
dark_hex.h
deep_sea.cc
deep_sea.h
efg_game.cc
Expand Down Expand Up @@ -278,6 +280,10 @@ add_executable(cursor_go_test cursor_go_test.cc
$<TARGET_OBJECTS:tests>)
add_test(cursor_go_test cursor_go_test)

add_executable(dark_hex_test dark_hex_test.cc ${OPEN_SPIEL_OBJECTS}
$<TARGET_OBJECTS:tests>)
add_test(dark_hex_test dark_hex_test)

add_executable(deep_sea_test deep_sea_test.cc ${OPEN_SPIEL_OBJECTS}
$<TARGET_OBJECTS:tests>)
add_test(deep_sea_test deep_sea_test)
Expand Down
272 changes: 272 additions & 0 deletions open_spiel/games/dark_hex.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,272 @@
// Copyright 2019 DeepMind Technologies Ltd. All rights reserved.
//
// 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.

#include "open_spiel/games/dark_hex.h"

#include <memory>
#include <utility>
#include <vector>

#include "open_spiel/abseil-cpp/absl/strings/str_cat.h"
#include "open_spiel/games/hex.h"
#include "open_spiel/spiel_utils.h"

namespace open_spiel {
namespace dark_hex {
namespace {

using hex::kCellStates;
using hex::kDefaultBoardSize;

using hex::CellState;
using hex::kMinValueCellState;

using hex::PlayerToState;
using hex::StateToString;

// Game Facts
const GameType kGameType{
/*short_name=*/"dark_hex",
/*long_name=*/"Dark Hex",
GameType::Dynamics::kSequential,
GameType::ChanceMode::kDeterministic,
GameType::Information::kImperfectInformation,
GameType::Utility::kZeroSum,
GameType::RewardModel::kTerminal,
/*max_num_players=*/2,
/*min_num_players=*/2,
/*provides_information_state_string=*/true,
/*provides_information_state_tensor=*/true,
/*provides_observation_string=*/true,
/*provides_observation_tensor=*/true,
/*parameter_specification=*/
{
{"obstype", GameParameter(std::string(kDefaultObsType))},
{"board_size", GameParameter(hex::kDefaultBoardSize)},
}};

std::shared_ptr<const Game> Factory(const GameParameters& params) {
return std::shared_ptr<const Game>(new DarkHexGame(params));
}

REGISTER_SPIEL_GAME(kGameType, Factory);

} // namespace

DarkHexState::DarkHexState(std::shared_ptr<const Game> game, int board_size,
ObservationType obs_type)
: State(game),
state_(game, board_size),
obs_type_(obs_type),
board_size_(board_size),
num_cells_(board_size * board_size),
bits_per_action_(num_cells_ + 1),
longest_sequence_(num_cells_ * 2 - 1) {
black_view_.resize(board_size * board_size, CellState::kEmpty);
white_view_.resize(board_size * board_size, CellState::kEmpty);
}

void DarkHexState::DoApplyAction(Action move) {
Player cur_player = CurrentPlayer(); // current player
auto& cur_view = (cur_player == 0 ? black_view_ : white_view_);

// Either occupied or not
if (state_.BoardAt(move) == CellState::kEmpty) {
state_.ApplyAction(move);
}

SPIEL_CHECK_EQ(cur_view[move], CellState::kEmpty);
cur_view[move] = state_.BoardAt(move);
action_sequence_.push_back(std::pair<int, Action>(cur_player, move));
}

std::vector<Action> DarkHexState::LegalActions() const {
if (IsTerminal()) return {};
std::vector<Action> moves;
const Player player = CurrentPlayer();
const auto& cur_view = (player == 0 ? black_view_ : white_view_);

for (Action move = 0; move < num_cells_; ++move) {
if (cur_view[move] == CellState::kEmpty) {
moves.push_back(move);
}
}

return moves;
}

std::string DarkHexState::ViewToString(Player player) const {
const auto& cur_view = (player == 0 ? black_view_ : white_view_);
std::string str;

// TODO: Hex - Dark-Hex Research sometimes experiments on different sized
// columns and rows for the game board. Therefore we need to add c-r option
// instead of board_size only.
int num_rows = board_size_, num_cols = board_size_;
for (int r = 0; r < num_rows; ++r) {
for (int c = 0; c < num_cols; ++c) {
absl::StrAppend(&str, StateToString(cur_view[r * num_cols + c]));
}
if (r < (num_rows - 1)) {
absl::StrAppend(&str, "\n");
}
}
return str;
}

std::string DarkHexState::ActionSequenceToString(Player player) const {
SPIEL_CHECK_GE(player, 0);
SPIEL_CHECK_LT(player, num_players_);

std::string str;
for (const auto& player_with_action : action_sequence_) {
if (player_with_action.first == player) {
absl::StrAppend(&str, player_with_action.first, ",");
absl::StrAppend(&str, player_with_action.second, " ");
} else if (obs_type_ == ObservationType::kRevealNumTurns) {
absl::StrAppend(&str, player_with_action.first, ",? ");
} else {
SPIEL_CHECK_EQ(obs_type_, ObservationType::kRevealNothing);
}
}
return str;
}

std::string DarkHexState::InformationStateString(Player player) const {
SPIEL_CHECK_GE(player, 0);
SPIEL_CHECK_LT(player, num_players_);
std::string str;
absl::StrAppend(&str, ViewToString(player), "\n");
absl::StrAppend(&str, history_.size(), "\n");
absl::StrAppend(&str, ActionSequenceToString(player));
return str;
}

void DarkHexState::InformationStateTensor(Player player,
absl::Span<float> values) const {
SPIEL_CHECK_GE(player, 0);
SPIEL_CHECK_LT(player, num_players_);

const auto& player_view = (player == 0 ? black_view_ : white_view_);

SPIEL_CHECK_EQ(values.size(), num_cells_ * kCellStates +
longest_sequence_ * (1 + bits_per_action_));
std::fill(values.begin(), values.end(), 0.);
for (int cell = 0; cell < num_cells_; ++cell) {
values[num_cells_ *
(static_cast<int>(player_view[cell]) - kMinValueCellState) +
cell] = 1.0;
}

// Encoding the sequence
int offset = num_cells_ * kCellStates;
for (const auto& player_with_action : action_sequence_) {
if (player_with_action.first == player) {
values[offset] = player_with_action.first;
values[offset + 1 + player_with_action.second] = 1.0;
} else if (obs_type_ == ObservationType::kRevealNumTurns) {
values[offset] = player_with_action.first;
values[offset + 1 + num_cells_ + 1] = 1.0;
} else {
SPIEL_CHECK_EQ(obs_type_, ObservationType::kRevealNothing);
}
offset += (1 + bits_per_action_);
}
}

std::string DarkHexState::ObservationString(Player player) const {
SPIEL_CHECK_GE(player, 0);
SPIEL_CHECK_LT(player, num_players_);
std::string observation = ViewToString(player);
if (obs_type_ == ObservationType::kRevealNumTurns) {
absl::StrAppend(&observation, "\nTotal turns: ", action_sequence_.size());
}
return observation;
}

void DarkHexState::ObservationTensor(Player player,
absl::Span<float> values) const {
SPIEL_CHECK_GE(player, 0);
SPIEL_CHECK_LT(player, num_players_);
SPIEL_CHECK_EQ(values.size(), game_->ObservationTensorSize());
std::fill(values.begin(), values.end(), 0.);

const auto& player_view = (player == 0 ? black_view_ : white_view_);
for (int cell = 0; cell < num_cells_; ++cell) {
values[num_cells_ *
(static_cast<int>(player_view[cell]) - kMinValueCellState) +
cell] = 1.0;
}

if (obs_type_ == ObservationType::kRevealNumTurns) {
values[num_cells_ * kCellStates + action_sequence_.size()] = 1.0;
}
}

std::unique_ptr<State> DarkHexState::Clone() const {
return std::unique_ptr<State>(new DarkHexState(*this));
}

void DarkHexState::UndoAction(Player player, Action move) {
Action last_move = action_sequence_.back().second;
SPIEL_CHECK_EQ(last_move, move);

if (state_.BoardAt(move) == PlayerToState(player)) {
state_.UndoAction(player, move);
} else {
}

auto& player_view = (player == 0 ? black_view_ : white_view_);
player_view[move] = CellState::kEmpty;
action_sequence_.pop_back();

history_.pop_back();
}

DarkHexGame::DarkHexGame(const GameParameters& params)
: Game(kGameType, params),
game_(std::static_pointer_cast<const hex::HexGame>(LoadGame(
"hex",
{{"board_size", GameParameter(ParameterValue<int>("board_size"))}}))),
board_size_(ParameterValue<int>("board_size")),
num_cells_(board_size_ * board_size_),
bits_per_action_(num_cells_ + 1),
longest_sequence_(num_cells_ * 2 - 1) {
std::string obs_type = ParameterValue<std::string>("obstype");
if (obs_type == "reveal-nothing") {
obs_type_ = ObservationType::kRevealNothing;
} else if (obs_type == "reveal-numturns") {
obs_type_ = ObservationType::kRevealNumTurns;
} else {
SpielFatalError(absl::StrCat("Unrecognized observation type: ", obs_type));
}
}

std::vector<int> DarkHexGame::InformationStateTensorShape() const {
return {num_cells_ * kCellStates +
longest_sequence_ * (1 + bits_per_action_)};
}

std::vector<int> DarkHexGame::ObservationTensorShape() const {
if (obs_type_ == ObservationType::kRevealNothing) {
return {num_cells_ * kCellStates};
} else if (obs_type_ == ObservationType::kRevealNumTurns) {
return {num_cells_ * kCellStates + longest_sequence_};
} else {
SpielFatalError("Uknown observation type");
}
}

} // namespace dark_hex
} // namespace open_spiel
Loading

0 comments on commit 306a32f

Please sign in to comment.