Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add dou dizhu #969

Merged
merged 14 commits into from
Dec 12, 2022
Merged

add dou dizhu #969

merged 14 commits into from
Dec 12, 2022

Conversation

rezunli96
Copy link
Contributor

Hi, I implemented dou dizhu.

After some thinking process, there are a few places that I implemented differently from what Wikipedia described. Specifically, for the hand category airplane+solo, I decided to permit cases like 333-444-555-7-7-7. E.g., pairs, trios can be kickers. For more details, please see my blog.

Also, I plan to implement mahjong in the future if nobody had claimed it yet.

@lanctot
Copy link
Collaborator

lanctot commented Dec 5, 2022

Nice one!!!

I put a comment in the makefile to help with the failing test.

@lanctot
Copy link
Collaborator

lanctot commented Dec 5, 2022

Also, I plan to implement mahjong in the future if nobody had claimed it yet.

It is unclaimed, so feel free. Thanks!

@lanctot lanctot mentioned this pull request Dec 5, 2022
Copy link
Contributor

@finbarrtimbers finbarrtimbers left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have not reviewed the .cc files yet; I will wait for you to make changes to the headers & tests first if that's ok.

open_spiel/games/dou_dizhu.h Outdated Show resolved Hide resolved
open_spiel/games/dou_dizhu.h Outdated Show resolved Hide resolved
open_spiel/games/dou_dizhu.h Outdated Show resolved Hide resolved
open_spiel/games/dou_dizhu.h Outdated Show resolved Hide resolved
open_spiel/games/dou_dizhu.h Outdated Show resolved Hide resolved
open_spiel/games/dou_dizhu/dou_dizhu_utils_test.cc Outdated Show resolved Hide resolved
open_spiel/games/dou_dizhu_test.cc Outdated Show resolved Hide resolved
open_spiel/games/dou_dizhu/dou_dizhu_utils.cc Outdated Show resolved Hide resolved
open_spiel/games/dou_dizhu/dou_dizhu_utils.cc Outdated Show resolved Hide resolved
open_spiel/games/dou_dizhu/dou_dizhu_utils.cc Outdated Show resolved Hide resolved
open_spiel/games/dou_dizhu/dou_dizhu_utils.cc Outdated Show resolved Hide resolved
open_spiel/games/dou_dizhu/dou_dizhu_utils.cc Outdated Show resolved Hide resolved
open_spiel/games/dou_dizhu.cc Outdated Show resolved Hide resolved
open_spiel/games/dou_dizhu.cc Show resolved Hide resolved
open_spiel/games/dou_dizhu.cc Outdated Show resolved Hide resolved
open_spiel/games/dou_dizhu.cc Outdated Show resolved Hide resolved
open_spiel/games/dou_dizhu.cc Show resolved Hide resolved
@rezunli96
Copy link
Contributor Author

@finbarrtimbers Thanks for the review! Here is one issue I am uncertain how to deal with: in several functions I use std::vector& as input type because during the procedures it may dynamically enlarge the vector. It cannot be known how long this vector will be beforehand. Do you know how to use absl::Span in case like this?

@lanctot
Copy link
Collaborator

lanctot commented Dec 8, 2022

@finbarrtimbers Thanks for the review! Here is one issue I am uncertain how to deal with: in several functions I use std::vector& as input type because during the procedures it may dynamically enlarge the vector. It cannot be known how long this vector will be beforehand. Do you know how to use absl::Span in case like this?

In this case you can pass it in via a pointer, std::vector<type>* var and then use pointer semantics (only use reference in function arguments when they are const.. this is a Google C++ style requirement).

absl::Span is meant-for read-only (so, not for cases like the one you're describing), and it often adds a lot of complexity for not much gain-- I would use it sparingly.

@rezunli96
Copy link
Contributor Author

@lanctot Thanks!
@finbarrtimbers Please take a look. Currently I change those read-only to const &, and those who will be overwritten to *. Please let me know if it is the right way!

@finbarrtimbers
Copy link
Contributor

Thanks for the review! Here is one issue I am uncertain how to deal with: in several functions I use std::vector& as input type because during the procedures it may dynamically enlarge the vector. It cannot be known how long this vector will be beforehand. Do you know how to use absl::Span in case like this?

Yes, sorry, I might have mentioned Span in the wrong context.

One slight difference to what Marc said- Span is fine when you're modifying the objects within the container, just not when you're modifying the container itself (i.e. resizing it).

If you're not modifying the container or any of the objects within it, specify the type as absl::Span<const T>, and it should automatically be converted (no need to do anything).

If you're modifying the objects within the container, then the type should be specified as absl::Span<T> and you need to call absl::MakeSpan on the container.

Span is an abstraction around fixed-size, contiguous blocks of memory (vector<T>, array<T>, etc.). It can be thought of as a convenience wrapper around the {pointer, length} tuple. So if you're resizing the underlying container, you shouldn't use Span (and I shouldn't have suggested it), as this invalidates the underlying pointer.

else SpielFatalError("Non valid rank");
}

std::string FormatSingleHand(const std::array<int, kNumRanks>& hand){
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So, this is an example of something that should be a Span. It should convert automatically.

}

// given a single-rank hand, map it to action id
int SingleRankHandToActionId(const std::array<int, kNumRanks>& hand){
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Another example of Span, as you're not modifying the hand container (or any of the members).

// given an arbitrary hand, search for possible single-rank hands
// if prev_action = kInvalidAction, search for all possible such hands
// otherwise, only search for those that are ranked higher than prev_action
void SearchSingleRankActions(std::vector<Action>* actions, const std::array<int, kNumRanks>& hand, int prev_action = kInvalidAction){
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This signature is a good example too- actions is great as a pointer, while hand should be a Span.

// and the result hand is stored in ans_hand reference
bool dfs_airplane_kicker(int chain_length, int depth,
int target_count, int& count, int max_search_rank,
int* used_rank, int* ans_hand,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think I was unclear/did a bad job reviewing this.

Originally, these were std::array<int, kNumRanks>&. I think that the type here should be absl::Span<int>.

You would convert them into a span like this:

 dfs_airplane_kicker(chain_length, depth+1, target_count, count, rank, absl::MakeSpan(used_rank), absl::MakeSpan(ans_hand), kicker_type)

// a dfs backtrack algorithm that found the action ids of all possible airplane combination
// the action ids are stored in action_ids reference
void dfs_add_all_airplane_kickers(int chain_head, int chain_length, int depth, int max_search_rank,
int* used_rank, const int* ans_hand,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Similarly, here, I think that you should make this signature:

absl::Span<int> used_rank, absl::Span<const int> ans_hand, std::vector<Action>* action_ids

(and call absl::MakeSpan(used_rank) on the calling code).

@finbarrtimbers
Copy link
Contributor

I went through and made some comments about specific signatures- please let me know if that helps or if you have any questions.

Thanks for working with me on this review- I think you're 95% of the way there, just a few changes to make, mostly just swapping some function signatures.

@rezunli96
Copy link
Contributor Author

rezunli96 commented Dec 8, 2022

Thanks that helps a lot! Some final questions.

  1. For a multidimensional array, should we transform it to absl::Span<absl::Span> or absl::Span<std::array>? If it is the former, is there a convenient constructor similar to absl::MakeSpan that can transform all inner array to Span? Or should we just write a loop and deal with each inner array explicitly?
  2. So using absl::Span is only required for function arguments right? For return type is it ok to use std::array<std::array>?

@finbarrtimbers
Copy link
Contributor

finbarrtimbers commented Dec 8, 2022

  1. For multidimensional arrays, I'd just not use Span, as those are very tied to the specific container types, and there's no convenient c'tor like MakeSpan.
  2. For your code, I think the only places I would use them are for inputs. But more generally, for outputs, I would use them only in place of const&. E.g. if you have a class which returns const std::vector<int>&, it should return absl::Span<const int>. But only in that case, as Span doesn't transfer ownership, so if you're returning vector<int>, you should not use Span.

@rezunli96
Copy link
Contributor Author

rezunli96 commented Dec 8, 2022

Thanks! The arguments should now all be changed to absl::Span if they can.

@finbarrtimbers
Copy link
Contributor

This looks great! I'll be merging this internally, and it should be merged at head soon. Thanks for working with me, @rezunli96!

@lanctot
Copy link
Collaborator

lanctot commented Dec 8, 2022

Thank you @finbarrtimbers for such a thorough review and great advice!

@lanctot lanctot added imported This PR has been imported and awaiting internal review. Please avoid any more local changes, thanks! merged internally The code is now submitted to our internal repo and will be merged in the next github sync. labels Dec 9, 2022
@OpenSpiel OpenSpiel merged commit b8aabe4 into google-deepmind:master Dec 12, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
imported This PR has been imported and awaiting internal review. Please avoid any more local changes, thanks! merged internally The code is now submitted to our internal repo and will be merged in the next github sync.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants