<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="3.10.0">Jekyll</generator><link href="https://jh2525.github.io/jh2525_.github.io/feed.xml" rel="self" type="application/atom+xml" /><link href="https://jh2525.github.io/jh2525_.github.io/" rel="alternate" type="text/html" /><updated>2026-06-15T03:44:41+00:00</updated><id>https://jh2525.github.io/jh2525_.github.io/feed.xml</id><title type="html">jh2525</title><subtitle>i am interested in reinforcement learning</subtitle><author><name>Junhyeok Oh</name></author><entry><title type="html">2.빅투 환경 만들기 - 보드와 환경 클래스</title><link href="https://jh2525.github.io/jh2525_.github.io/bigtwo/Board-and-Environment-Class/" rel="alternate" type="text/html" title="2.빅투 환경 만들기 - 보드와 환경 클래스" /><published>2024-02-22T00:00:00+00:00</published><updated>2024-02-22T00:00:00+00:00</updated><id>https://jh2525.github.io/jh2525_.github.io/bigtwo/Board%20and%20Environment%20Class</id><content type="html" xml:base="https://jh2525.github.io/jh2525_.github.io/bigtwo/Board-and-Environment-Class/"><![CDATA[<h1 id="1개요">1.개요</h1>
<p>여기서는 앞서 만든 클래스들을 통해서 빅투의 환경을 완성하는 것에 대해서 설명을 하도록한다. 보드 클래스는 환경 클래스와 독립적으로 작동하며, 단순히 시작할 때 플레이어에게 카드를 나눠주고, 플레이어의 손패를 관리해주는 역할을한다. 빅투 환경 클래스에서는 보드 클래스를 이용하여, 실제 플레이어들이 할 수 있는 행동을 관리한다.</p>

<h1 id="2보드-클래스">2.보드 클래스</h1>
<p>보드 클래스는 단순히 플레이어들의 카드를 관리하고, 초기화시 랜덤으로 카드를 분배하는 역할을 한다. 앞서 손패 클래스와 크게 다른 부분은 없으므로 간단하게 코드만을 제공한다.</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">class</span> <span class="nc">Board</span><span class="p">():</span>
    <span class="k">def</span> <span class="nf">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
        <span class="bp">self</span><span class="p">.</span><span class="n">initialize</span><span class="p">()</span>
    
    <span class="k">def</span> <span class="nf">initialize</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">seed</span><span class="p">:</span> <span class="n">Optional</span><span class="p">[</span><span class="nb">int</span><span class="p">]</span> <span class="o">=</span> <span class="bp">None</span><span class="p">,</span> <span class="n">player_fix_cards</span><span class="p">:</span> <span class="n">Optional</span><span class="p">[</span><span class="n">np</span><span class="p">.</span><span class="n">ndarray</span><span class="p">]</span> <span class="o">=</span> <span class="bp">None</span><span class="p">):</span>
        <span class="k">if</span> <span class="n">player_fix_cards</span> <span class="ow">is</span> <span class="bp">None</span><span class="p">:</span>
            <span class="n">player_fix_cards</span> <span class="o">=</span> <span class="n">np</span><span class="p">.</span><span class="n">zeros</span><span class="p">((</span><span class="mi">4</span><span class="p">,</span> <span class="mi">4</span><span class="o">*</span><span class="mi">13</span><span class="p">))</span>
        <span class="k">if</span> <span class="ow">not</span> <span class="n">seed</span> <span class="ow">is</span> <span class="bp">None</span><span class="p">:</span>
            <span class="n">np</span><span class="p">.</span><span class="n">random</span><span class="p">.</span><span class="n">seed</span><span class="p">(</span><span class="n">seed</span><span class="p">)</span>
        
        <span class="n">n_fix_card</span> <span class="o">=</span> <span class="n">player_fix_cards</span><span class="p">.</span><span class="nb">sum</span><span class="p">(</span><span class="n">axis</span><span class="o">=</span><span class="mi">1</span><span class="p">)</span>
        <span class="n">fix_cards</span> <span class="o">=</span> <span class="n">player_fix_cards</span><span class="p">.</span><span class="nb">sum</span><span class="p">(</span><span class="n">axis</span><span class="o">=</span><span class="mi">0</span><span class="p">)</span>

        <span class="n">shuffled_cards</span> <span class="o">=</span> <span class="n">np</span><span class="p">.</span><span class="n">arange</span><span class="p">(</span><span class="mi">13</span><span class="o">*</span><span class="mi">4</span><span class="p">)[</span><span class="o">~</span><span class="n">fix_cards</span><span class="p">.</span><span class="n">astype</span><span class="p">(</span><span class="nb">bool</span><span class="p">)]</span>
        <span class="n">np</span><span class="p">.</span><span class="n">random</span><span class="p">.</span><span class="n">shuffle</span><span class="p">(</span><span class="n">shuffled_cards</span><span class="p">)</span>

        <span class="n">hands</span> <span class="o">=</span> <span class="n">np</span><span class="p">.</span><span class="n">zeros</span><span class="p">((</span><span class="mi">4</span><span class="p">,</span> <span class="mi">52</span><span class="p">))</span> <span class="o">+</span> <span class="n">player_fix_cards</span>

        <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">4</span><span class="p">):</span>
            <span class="n">start</span> <span class="o">=</span> <span class="mi">0</span> <span class="k">if</span> <span class="n">i</span> <span class="o">==</span> <span class="mi">0</span> <span class="k">else</span> <span class="n">end</span>
            <span class="n">end</span> <span class="o">=</span> <span class="mi">13</span> <span class="o">-</span> <span class="nb">int</span><span class="p">(</span><span class="n">n_fix_card</span><span class="p">[</span><span class="n">i</span><span class="p">])</span> <span class="o">+</span> <span class="p">(</span><span class="n">end</span> <span class="k">if</span> <span class="n">i</span> <span class="o">!=</span> <span class="mi">0</span> <span class="k">else</span> <span class="mi">0</span><span class="p">)</span>
            <span class="n">hands</span><span class="p">[</span><span class="n">i</span><span class="p">,</span> <span class="n">shuffled_cards</span><span class="p">[</span><span class="n">start</span><span class="p">:</span><span class="n">end</span><span class="p">]]</span> <span class="o">=</span> <span class="mi">1</span>

        <span class="bp">self</span><span class="p">.</span><span class="n">hands</span> <span class="p">:</span> <span class="n">List</span><span class="p">[</span><span class="n">Hand</span><span class="p">]</span> <span class="o">=</span> <span class="p">[</span><span class="n">Hand</span><span class="p">(</span><span class="n">torch</span><span class="p">.</span><span class="n">from_numpy</span><span class="p">(</span><span class="n">hands</span><span class="p">[</span><span class="n">i</span><span class="p">]).</span><span class="nb">float</span><span class="p">().</span><span class="n">view</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">4</span><span class="p">,</span> <span class="mi">13</span><span class="p">))</span> <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="n">players</span><span class="p">)]</span>

        <span class="k">return</span> <span class="bp">self</span>
    
    <span class="k">def</span> <span class="nf">get_card_tensor</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">player</span><span class="p">:</span> <span class="nb">int</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="n">torch</span><span class="p">.</span><span class="n">Tensor</span><span class="p">:</span>
        <span class="k">return</span> <span class="bp">self</span><span class="p">.</span><span class="n">hands</span><span class="p">[</span><span class="n">player</span><span class="p">].</span><span class="n">card_tensor</span>
    
    <span class="k">def</span> <span class="nf">get_n_card</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">player</span><span class="p">:</span> <span class="nb">int</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="nb">int</span><span class="p">:</span>
        <span class="k">return</span> <span class="bp">self</span><span class="p">.</span><span class="n">hands</span><span class="p">[</span><span class="n">player</span><span class="p">].</span><span class="n">n_card</span>
    
    <span class="k">def</span> <span class="nf">get_all_rankings</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">player</span><span class="p">:</span> <span class="nb">int</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="n">List</span><span class="p">[</span><span class="n">HandRanking</span><span class="p">]:</span>
        <span class="k">return</span> <span class="bp">self</span><span class="p">.</span><span class="n">hands</span><span class="p">[</span><span class="n">player</span><span class="p">].</span><span class="n">get_all_hand_rankings</span><span class="p">()</span>
    
    <span class="k">def</span> <span class="nf">discard_cards</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">player</span><span class="p">:</span> <span class="nb">int</span><span class="p">,</span> <span class="n">card_tensor</span><span class="p">:</span> <span class="n">torch</span><span class="p">.</span><span class="n">Tensor</span><span class="p">):</span>
        <span class="bp">self</span><span class="p">.</span><span class="n">hands</span><span class="p">[</span><span class="n">player</span><span class="p">].</span><span class="n">discard_cards</span><span class="p">(</span><span class="n">card_tensor</span><span class="p">)</span>
        <span class="k">return</span> <span class="bp">self</span>

    <span class="k">def</span> <span class="nf">get_all_hand_rankings_stronger_than</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">player</span><span class="p">:</span> <span class="nb">int</span><span class="p">,</span> <span class="n">hand_suit</span><span class="p">:</span> <span class="n">HandRanking</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="n">List</span><span class="p">[</span><span class="n">HandRanking</span><span class="p">]:</span>
        <span class="k">return</span> <span class="bp">self</span><span class="p">.</span><span class="n">hands</span><span class="p">[</span><span class="n">player</span><span class="p">].</span><span class="n">get_all_hand_rankings_stronger_than</span><span class="p">(</span><span class="n">hand_suit</span><span class="p">)</span>

</code></pre></div></div>

<h1 id="3환경-클래스">3.환경 클래스</h1>
<p>결국 이때까지 만든 것들은 환경 클래스를 만들기 위함이라고해도 과언이 아니다. 기본적으로 환경 클래스는 다음과 같은 기능을 제공한다.</p>

<ul>
  <li>게임 진행 : <strong><em>step</em></strong> 함수를 통해서, 빅투의 규칙에 따라서 게임을 진행한다.</li>
  <li>관측값 제공 : 현재 게임 상황의 관측값을 제공해준다. 추후 관측값의 변화에 유연하게 다루기 위하여 <strong>Observer</strong> 클래스를 통해서 관측이 이루어진다.</li>
  <li>게임기록 저장 : <strong>Observer</strong> 클래스가 이전 플레이어의 행동에 따라서 관측값을 반환해주기 위해서 각 플레이어가 했던 행동들을 저장한다.</li>
</ul>

<p>각 기능들을 코드와 함께 자세히 살펴보자</p>

<h2 id="1게임-진행">1.게임 진행</h2>
<p>여기선 빅투 클래스가 게임을 진행하는 방법에 대해서 알아본다. 만약 현재 나와있는 가장 강한 족보를 $r$이라고하자. 그러면, 플레이어가 가능한 행동의 수는 플레이어의 손패에서 가능한 모든 족보들을 $R_p$라고 했을 때, $\mathcal{A} = {r’ \in R_p \mid r &lt; r’}$이 된다. 또한 $\mathcal{A}$는 유한하므로, $\mathcal{A} = {a_1, a_2, \dots, a_n}$로 일관성있게 표현가능하고, 우리는 $i$-번째 행동을 족보 $a_i$를 버리는 것으로 정의할 것이다. 이를 파이썬 코드로 나타내면 다음과 같다.</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">#현재 나와있는 가장 강한 족보보다 강한 현재 행동하는 플레이어들의 족보를 가져온다.
</span><span class="n">available_rankings</span> <span class="o">=</span> <span class="bp">self</span><span class="p">.</span><span class="n">board</span><span class="p">.</span><span class="n">get_all_hand_rankings_stronger_than</span><span class="p">(</span><span class="bp">self</span><span class="p">.</span><span class="n">turn</span><span class="p">,</span> <span class="bp">self</span><span class="p">.</span><span class="n">strongest_hand_ranking</span><span class="p">)</span> 
<span class="c1">#플레이어가 행동을 취할 시 버릴 족보
</span><span class="n">action_ranking</span> <span class="o">=</span> <span class="n">available_rankings</span><span class="p">[</span><span class="n">action</span><span class="p">]</span> 
</code></pre></div></div>

<p>플레이어가 버릴 족보를 정했다고 가정해보자. 해당 족보가 Pass이면, 빅 투의 규칙에 따라서, 나와있는 가장 강한 족보는 바뀌지 않을 것이다. 하지만, 만약 플레이어가 Pass를 했을 때, 플레이어가 3번째 연속적으로 Pass를 한 것이면 나와있는 가장 강한 족보는 None이 되어야한다. (다음 플레이어가 첫 시작이므로) 플레이어가 Pass를 하지 않았다면, 당연히 나와있는 족보보다 더 강한 족보를 냈다는 것이므로 빅투의 규칙에 따라서 나와있는 가장 강한 족보는 갱신해야한다. 이를 파이썬 코드로 나타내면 다음과 같다.</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">discard</span> <span class="o">=</span> <span class="n">action_ranking</span><span class="p">.</span><span class="n">cards</span> <span class="c1">#해당 족보를 버릴 시 버려질 카드 행렬
</span><span class="n">act_pass</span> <span class="o">=</span> <span class="p">(</span><span class="n">action_ranking</span><span class="p">.</span><span class="n">suit</span> <span class="o">==</span> <span class="n">Ranking</span><span class="p">.</span><span class="n">PASS</span><span class="p">)</span> <span class="c1">#플레이어가 택한 행동이 패스인가?
</span>
<span class="k">if</span> <span class="n">act_pass</span><span class="p">:</span>
    <span class="bp">self</span><span class="p">.</span><span class="n">pass_count</span> <span class="o">+=</span> <span class="mi">1</span> <span class="c1">#만약 패스한 경우, 패스 카운트를 1올려준다 이 패스 카운트가 3이되면, 한 플레이어를 제외하고 나머지 모든 플레이어가 연속적으로 패스했다는 것이다.
</span><span class="k">else</span><span class="p">:</span>
    <span class="bp">self</span><span class="p">.</span><span class="n">pass_count</span> <span class="o">=</span> <span class="mi">0</span> <span class="c1">#패스가 아니면 연속적인 패스가 끊긴 것이므로 패스 카운트를 초기화해준다.
</span>    <span class="bp">self</span><span class="p">.</span><span class="n">strongest_hand_ranking</span> <span class="o">=</span> <span class="n">action_ranking</span> <span class="c1">#패스가 아니면 플레이어가 해당 족보를 버렸다는 것이므로, 현재 나와있는 가장 강한 족보보다 강한 족보를 버린 것이다. 따라서 나와있는 가장 강한 족보를 플레이어가 택한 족보로 변경한다.
</span>
<span class="c1">#만약 3번 연속으로 패스가 된 경우
</span><span class="k">if</span> <span class="bp">self</span><span class="p">.</span><span class="n">pass_count</span> <span class="o">&gt;=</span> <span class="mi">3</span><span class="p">:</span>
    <span class="bp">self</span><span class="p">.</span><span class="n">strongest_hand_ranking</span> <span class="o">=</span> <span class="n">HandRanking</span><span class="p">.</span><span class="n">NONE</span> <span class="c1">#나와있는 가장 강한 족보는 None이 된다.
</span>    <span class="bp">self</span><span class="p">.</span><span class="n">pass_count</span> <span class="o">=</span> <span class="mi">0</span> <span class="c1">#Pass 카운트를 초기화 해준다.
</span>
<span class="c1">#Board 클래스를 통해서 플레이어가 카드를 버린 것을 처리한다.
</span><span class="k">if</span> <span class="ow">not</span> <span class="n">act_pass</span><span class="p">:</span>
    <span class="bp">self</span><span class="p">.</span><span class="n">board</span><span class="p">.</span><span class="n">discard_cards</span><span class="p">(</span><span class="bp">self</span><span class="p">.</span><span class="n">turn</span><span class="p">,</span> <span class="n">discard</span><span class="p">)</span>
    <span class="k">if</span> <span class="bp">self</span><span class="p">.</span><span class="n">board</span><span class="p">.</span><span class="n">hands</span><span class="p">[</span><span class="bp">self</span><span class="p">.</span><span class="n">turn</span><span class="p">].</span><span class="n">card_tensor</span><span class="p">.</span><span class="nb">sum</span><span class="p">().</span><span class="n">item</span><span class="p">()</span> <span class="o">&lt;</span> <span class="mi">1</span><span class="p">:</span>
        <span class="n">terminated</span> <span class="o">=</span> <span class="bp">True</span>
    
<span class="c1">#플레이어의 턴을 넘긴다
</span><span class="bp">self</span><span class="p">.</span><span class="n">turn</span> <span class="o">=</span> <span class="p">(</span><span class="bp">self</span><span class="p">.</span><span class="n">turn</span> <span class="o">+</span> <span class="mi">1</span><span class="p">)</span> <span class="o">%</span> <span class="n">players</span>
</code></pre></div></div>

<h2 id="2관측값-제공">2.관측값 제공</h2>
<p>앞서 언급했듯이 관측값에 따른 성능 비교를 위해서, 빅투 환경에서의 관측값은 Observer 클래스를 통해서 행해진다. Observer 클래스의 추상 클래스는 다음과 같다</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">class</span> <span class="nc">Observer</span><span class="p">():</span>
    <span class="k">def</span> <span class="nf">get_observation</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">logs</span><span class="p">,</span> <span class="n">card_tensor</span><span class="p">:</span> <span class="n">torch</span><span class="p">.</span><span class="n">Tensor</span><span class="p">,</span> <span class="n">strongest_ranking</span><span class="p">:</span> <span class="n">HandRanking</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="n">torch</span><span class="p">.</span><span class="n">Tensor</span><span class="p">:</span>
        <span class="k">return</span> 
</code></pre></div></div>

<p>단순히 환경에서의 로그와, 플레이어의 손패와, 나와있는 가장 강한 족보를 인수로 받고, 관측값을 반환한다. 빅투 클래스는 Observer클래스의 객체를 통해서, step시 관측값을 반환해준다.</p>

<h2 id="3게임-기록-저장">3.게임 기록 저장</h2>
<p>step시 플레이어, 손패 행렬, 가장 강한 족보의 dictionary를 리스트 형태로 단순히 저장해둔다. Observer가 observation을 만들거나, 플레이 기록을 확인하기 위해서 사용한다.</p>

<h2 id="4전체-코드">4.전체 코드</h2>
<details>
<summary>코드 보기</summary>

```python

class Big2Env():
    def __init__(self, observer: Observer, render = False):
        self.board: Board = Board()
        self.logs = []
        self.observer = observer
        self.strongest_hand_ranking: HandRanking
        self.pass_count = 0

    def reset(self, seed: Optional[int] = None, player_fix_cards: Optional[np.ndarray] = None):
        if player_fix_cards is None:
            player_fix_cards = np.zeros((4, 4*13))
        self.board.initialize(seed, player_fix_cards)

        #find the player who discards cards at first
        for i in range(players):
            if self.board.hands[i].card_tensor[0][sn_index('diamond' , '3')].item() &gt; 0:
                self.turn = i
                break
        
        self.pass_count = 0
        self.strongest_hand_ranking = HandRanking.NONE
        self.logs = []
        for i in range(4):
            self.append_log(
                player = (self.turn + i)  % players,
                card_tensor = self.board.hands[(self.turn + i) % players].card_tensor,
                discard = torch.zeros(1, 4, 13),
                act_pass = False,
                strongest_ranking = HandRanking.NONE
            )
        observation = self.observer.get_observation(self.logs, self.board.hands[self.turn])
        

        return observation, {}
    
    def step(self, action: int):
        
        available_rankings = self.board.get_all_hand_rankings_stronger_than(self.turn, self.strongest_hand_ranking)
        action_ranking = available_rankings[action]

        discard = action_ranking.card_tensor
        act_pass = (action_ranking.ranking == Ranking.PASS)

        if act_pass:
            self.pass_count += 1
        else:
            self.pass_count = 0
            self.strongest_hand_ranking = action_ranking
        
        self.append_log(
            player = self.turn, 
            card_tensor = self.board.get_card_tensor(self.turn),
            discard = discard, 
            act_pass = act_pass, 
            strongest_ranking = self.strongest_hand_ranking
        )

        terminated = False
        info = {}
        reward = discard.sum().item() / 5.0
        truncated = False
        next_observation = None

        #if passed 3 times in a row
        if self.pass_count &gt;= 3:
            self.strongest_hand_ranking = HandRanking.NONE
            self.pass_count = 0

        #discard
        if not act_pass:
            self.board.discard_cards(self.turn, discard)
            if self.board.get_n_card(self.turn) &lt; 1:
                terminated = True
        
        #turn next player
        self.turn = (self.turn + 1) % players
        next_observation = self.observer.get_observation(
            logs = self.logs, 
            player_hand = self.board.hands[self.turn], 
        )
        
        return next_observation, reward, terminated, truncated, info
    

    def append_log(self, player, card_tensor, discard, act_pass, strongest_ranking):
        self.logs.append({'player':player, 'card_tensor':card_tensor, 'discard':discard, 'pass':act_pass, 'strongest_ranking':strongest_ranking})

        return self
    
    def hand_to_str(self, card_tensor: torch.Tensor):
        shape = ['D', 'C', 'H', 'S']
        result = ''
        for i in range(4):
            numbers = [str(i.item()) for i in torch.where(card_tensor[0][i] &gt; 0)[0] + 2]
            if len(numbers) &gt; 0:
                result += shape[i]
            for n in numbers:
                result += n + ' '
        return result
        
    
```

</details>
<p>&lt;/br&gt;</p>]]></content><author><name>Junhyeok Oh</name></author><category term="BigTwo" /><category term="BigTwo" /><category term="RL" /><summary type="html"><![CDATA[2.Creating Big Two environment - Board and environment class]]></summary></entry><entry><title type="html">3.빅투 학습하기 - 빅투의 어려움</title><link href="https://jh2525.github.io/jh2525_.github.io/bigtwo/Difficult-of-Big-Two/" rel="alternate" type="text/html" title="3.빅투 학습하기 - 빅투의 어려움" /><published>2024-02-22T00:00:00+00:00</published><updated>2024-02-22T00:00:00+00:00</updated><id>https://jh2525.github.io/jh2525_.github.io/bigtwo/Difficult%20of%20Big%20Two</id><content type="html" xml:base="https://jh2525.github.io/jh2525_.github.io/bigtwo/Difficult-of-Big-Two/"><![CDATA[<h1 id="1개요">1.개요</h1>
<p>여기서는 빅투 환경에서 학습을 진행하기 앞서 앞으로 해결할 빅투의 학습 시 어려움에 대해서 알아보자.</p>

<h1 id="2행동의-다양성">2.행동의 다양성</h1>
<p>빅투에서 패를 받았을 때 플러시의 경우의 수는 ${13 \choose 5} = 1287$이다. 이렇게 많은 족보의 경우의 수에 대하여 모든 행동을 정의하기에는 무리가 있어보인다. 그렇다면, 빅투에서의 action은 어떤 방식으로 다룰 수 있을까?</p>

<p>이를 다룬 포스트는 아래를 참고하자.</p>

<p><a href="https://github.com/jh2525/ACM">Action state map</a></p>

<h1 id="3보상의-설정-문제">3.보상의 설정 문제</h1>
<p>우리의 목표는 최종 칩 갯수에서의 <strong>순위보상</strong>을 최대화 하는 것이다. 그렇지만, 곧바로 이를 보상으로 설정하기엔 <strong>희소 보상 문제</strong>에 직결하게 된다. 또한, 어떤 한 플레이어의 칩의 갯수가 다 떨어지기 위해서는 많은 행동을 반복해야한다. 칩의 이동 개수도 불안정한데, 한 번의 게임당 칩의 변화는 최소 3개에서, 최대 $2^4\times(14 + 13\times2)  = 640$개 까지 될 수도 있다. 대부분의 게임은 칩의 개수가 10개 정도로 변할 것이라고 생각되지만, 칩의 개수의 변화는 매우 다양하기 때문에, 이 역시 학습 불안정성을 초래할 것이다.</p>

<p>이를 다룬 포스트는 아래를 참고하자.</p>

<p><a href=":">Global reward prediction</a></p>]]></content><author><name>Junhyeok Oh</name></author><category term="BigTwo" /><category term="BigTwo" /><category term="RL" /><summary type="html"><![CDATA[2.Reinforcement learning in Big Two - difficult of Big Two]]></summary></entry><entry><title type="html">2.빅투 환경 만들기 - 손패 클래스</title><link href="https://jh2525.github.io/jh2525_.github.io/bigtwo/HandClass/" rel="alternate" type="text/html" title="2.빅투 환경 만들기 - 손패 클래스" /><published>2024-02-22T00:00:00+00:00</published><updated>2024-02-22T00:00:00+00:00</updated><id>https://jh2525.github.io/jh2525_.github.io/bigtwo/HandClass</id><content type="html" xml:base="https://jh2525.github.io/jh2525_.github.io/bigtwo/HandClass/"><![CDATA[<h1 id="1개요">1.개요</h1>
<p>여기서는 빅투 환경에서의 사용될 손패 클래스에 대해서 소개한다. 기본적으로 플레이어는 족보에 있는 카드의 조합으로만 손패를 버릴 수 있기 때문에, 가지고 있는 손패가 어떤 족보를 만족시키는지를 구현하는 것은 매우 필수적인 일이다. 여기에선, Pytorch Tensor를 기반으로 손패에서 카드를 버리고, 카드를 얻을 때마다 자동으로 가능한 족보를 나타내주는 손패 클래스의 구현에 대해서 설명한다.</p>

<h1 id="2족보의-추상화">2.족보의 추상화</h1>
<p>빅투의 스트레이트 (플러시)를 제외한 모든 족보들은 손패 행렬의 행방향의 합 또는 열방향의 합으로 해당 족보를 충족하는지를 알 수 있다. 예를 들어서 1행(슈트 D)의 모든 원소들의 합이 5이상이면 다이아몬드로 이루어진 플러시를 만들 수 있단 뜻이며, 3열(끗수 4)의 원소들의 합이 3이상이면 끗수 4로 이루어진 싱글, 페어, 트리플을 낼 수 있다는 것을 의미한다.
<img src="https://github.com/jh2525/jh2525.github.io/assets/160830734/285eef28-3f24-48d7-b8dc-6a8428b2acd2" alt="BigTwoSuit" /></p>

<p>(D4589J C1 S1의 손패 행렬)</p>

<p>따라서, 다음과 같이 열방향으로 n개 이상인 모든 경우의 수를 구할 수 있는 함수를 정의하자. 이 때, 추후 행방향으로 구할 때도, 일반성을 잃지 않고 같은 방식의 함수를 이용할 수 있도록 손패 행렬의 전치 여부를 결정하는 transpose 인자를 추가해준다.</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">get_n_common_cards</span><span class="p">(</span><span class="n">card_tensor</span><span class="p">:</span> <span class="n">torch</span><span class="p">.</span><span class="n">Tensor</span><span class="p">,</span> <span class="n">n</span><span class="p">:</span> <span class="nb">int</span><span class="p">,</span> <span class="n">transpose</span><span class="p">:</span> <span class="nb">bool</span> <span class="o">=</span> <span class="bp">False</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="n">List</span><span class="p">[</span><span class="n">torch</span><span class="p">.</span><span class="n">Tensor</span><span class="p">]:</span>
    <span class="k">if</span> <span class="n">transpose</span><span class="p">:</span>
        <span class="n">card_tensor</span> <span class="o">=</span> <span class="n">card_tensor</span><span class="p">.</span><span class="n">permute</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">1</span><span class="p">)</span>

    <span class="n">n_sums</span> <span class="o">=</span> <span class="n">card_tensor</span><span class="p">.</span><span class="nb">sum</span><span class="p">(</span><span class="n">dim</span><span class="o">=</span><span class="mi">1</span><span class="p">)</span>
    <span class="n">_</span><span class="p">,</span> <span class="n">numbers</span> <span class="o">=</span> <span class="n">torch</span><span class="p">.</span><span class="n">where</span><span class="p">(</span><span class="n">n_sums</span> <span class="o">&gt;=</span> <span class="n">n</span><span class="p">)</span>

    <span class="n">result</span> <span class="o">=</span> <span class="p">[]</span>

    <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="n">numbers</span><span class="p">:</span>
        <span class="n">indicies</span><span class="p">,</span>  <span class="o">=</span> <span class="n">torch</span><span class="p">.</span><span class="n">where</span><span class="p">(</span><span class="n">card_tensor</span><span class="p">[</span><span class="mi">0</span><span class="p">,</span> <span class="p">:,</span> <span class="n">i</span><span class="p">])</span>
        <span class="n">cases</span> <span class="o">=</span> <span class="n">torch</span><span class="p">.</span><span class="n">combinations</span><span class="p">(</span><span class="n">indicies</span><span class="p">,</span> <span class="n">n</span><span class="p">)</span>
        <span class="k">for</span> <span class="n">j</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="n">cases</span><span class="p">)):</span>
            <span class="n">n_same_cards</span> <span class="o">=</span> <span class="n">torch</span><span class="p">.</span><span class="n">zeros_like</span><span class="p">(</span><span class="n">card_tensor</span><span class="p">)</span>
            <span class="n">n_same_cards</span><span class="p">[</span><span class="mi">0</span><span class="p">,</span> <span class="n">cases</span><span class="p">[</span><span class="n">j</span><span class="p">],</span> <span class="n">i</span><span class="p">]</span> <span class="o">=</span> <span class="mf">1.0</span>
            
            <span class="k">if</span> <span class="n">transpose</span><span class="p">:</span>
                <span class="n">n_same_cards</span> <span class="o">=</span> <span class="n">n_same_cards</span><span class="p">.</span><span class="n">permute</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">1</span><span class="p">)</span>
            
            <span class="n">result</span><span class="p">.</span><span class="n">append</span><span class="p">(</span><span class="n">n_same_cards</span><span class="p">)</span>
        
    <span class="k">return</span> <span class="n">result</span>
</code></pre></div></div>

<p>그러면 스트레이트를 제외한 모든 족보들은 다음과 같은 방법으로 구할 수 있다:</p>
<ul>
  <li>싱글, 페어, 트리플 : 각각 $n = 1, 2, 3$인 경우에 해당한다.</li>
  <li>풀하우스, 포카드 : 기존의 카드 행렬을 $H$라고 하고, 각각 $n = 3, 4$ 일 때를 모두 구하고 해당 카드 행렬들을 ${h_0, h_1, …, h_n}$이라고하자. 다시 ${H - h_0, …, H - h_n}$ 에대해서 $n = 2, 1$ 일때의 $i$번째 카드 행렬에 대해 구해진 카드 행렬들을 $j_i^0, j_i^1, …, j_i^{m_i}$이라고하자. 그러면 $h_0 + j_0^0, h_0 + j_0^1, …, h_0 + j_0^{m_0} + … + h_n + j_n^{m_n}$이 풀하우스, 포카드에 관한 가능한 모든 카드 행렬이다. 이를 코드로 나타내면 다음과 같다: (풀하우스는 $n = 3, m = 2$, 포카드는 $n = 4, m = 1$인 경우에 해당한다.)
    <div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">get_n_m_common_cards</span><span class="p">(</span><span class="n">card_tensor</span><span class="p">:</span> <span class="n">torch</span><span class="p">.</span><span class="n">Tensor</span><span class="p">,</span> <span class="n">n</span><span class="p">:</span> <span class="nb">int</span><span class="p">,</span> <span class="n">m</span><span class="p">:</span> <span class="nb">int</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="n">List</span><span class="p">[</span><span class="n">torch</span><span class="p">.</span><span class="n">Tensor</span><span class="p">]:</span>
  <span class="n">result</span> <span class="o">=</span> <span class="p">[]</span>
  <span class="n">n_common_cards</span> <span class="o">=</span> <span class="n">get_n_common_cards</span><span class="p">(</span><span class="n">card_tensor</span><span class="p">,</span> <span class="n">n</span><span class="p">)</span>
  <span class="k">for</span> <span class="n">n_card</span> <span class="ow">in</span> <span class="n">n_common_cards</span><span class="p">:</span>
      <span class="n">m_common_cards</span> <span class="o">=</span> <span class="n">get_n_common_cards</span><span class="p">(</span><span class="n">card_tensor</span> <span class="o">-</span> <span class="n">n_card</span><span class="p">,</span> <span class="n">m</span><span class="p">)</span>
      <span class="k">for</span> <span class="n">m_card</span> <span class="ow">in</span> <span class="n">m_common_cards</span><span class="p">:</span>
          <span class="n">result</span><span class="p">.</span><span class="n">append</span><span class="p">(</span><span class="n">n_card</span> <span class="o">+</span> <span class="n">m_card</span><span class="p">)</span>
  <span class="k">return</span> <span class="n">result</span>
</code></pre></div>    </div>
  </li>
  <li>플러시 : 손패 행렬의 전치 행렬에 대해서 $n = 5$ 일때의 카드 행렬들을 구한다음에, 다시 각 행렬들을 전치시키면 된다. 앞서 만든 함수에서, transpose인자를 True로 해주면된다.
    <h2 id="스트레이트">스트레이트</h2>
    <p>스트레이트의 경우는 다른 족보들과는 다르게 끗수가 연속적이어야 된다는 조건이 붙는다. 또한 백스트레이트라는 예외가 존재한다. 먼저 스트레이트의 경우의 수를 구하기 위해서는 단순히 루프문을 이용해서 결과를 구해도 되지만, 깔끔한 방법은 아니라고 생각한다. 따라서, 먼저 각 끗수에 대해서 카드가 존재(1)하는지를 안하는지(0)를 나타내는 (13,) 크기의 벡터를 구하고(5, ) 크기의 모든 성분의 값이 1인 벡터를 필터로 사용하여 합성곱 연산을 통해서, 값이 5이상인 위치에서 스트레이트가 가능함을 알 수 있다. 또한 백스트레이트의 경우 간단하게 손패 행렬의 1번째 열을 마지막 열에 다시 추가한 행렬을 통해서 합성곱 연산을 진행해주면, 백스트레이트의 경우도 간단하게 구할 수 있다. 다음은 코드와 해당 과정을 설명해주는 사진이다:
<img src="https://github.com/jh2525/jh2525.github.io/assets/160830734/2815fa6a-4faf-4830-ba51-82cda82f68e0" alt="straight2" /></p>
  </li>
</ul>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">straight_filter</span> <span class="o">=</span> <span class="n">torch</span><span class="p">.</span><span class="n">full</span><span class="p">((</span><span class="mi">1</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">5</span><span class="p">,),</span> <span class="mf">1.0</span><span class="p">)</span>

<span class="k">def</span> <span class="nf">get_straight</span><span class="p">(</span><span class="n">card_tensor</span><span class="p">:</span> <span class="n">torch</span><span class="p">.</span><span class="n">Tensor</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="n">List</span><span class="p">[</span><span class="n">torch</span><span class="p">.</span><span class="n">Tensor</span><span class="p">]:</span>
    <span class="n">concat_tensor</span> <span class="o">=</span> <span class="n">torch</span><span class="p">.</span><span class="n">concat</span><span class="p">([</span><span class="n">card_tensor</span><span class="p">[:,</span> <span class="p">:,</span> <span class="o">-</span><span class="mi">1</span><span class="p">:],</span> <span class="n">card_tensor</span><span class="p">],</span> <span class="n">dim</span><span class="o">=-</span><span class="mi">1</span><span class="p">)</span>
    <span class="n">column_sum</span> <span class="o">=</span> <span class="p">(</span><span class="n">concat_tensor</span><span class="p">.</span><span class="nb">sum</span><span class="p">(</span><span class="n">dim</span><span class="o">=</span><span class="mi">1</span><span class="p">)</span> <span class="o">&gt;</span> <span class="mi">0</span><span class="p">).</span><span class="nb">float</span><span class="p">()</span>
    <span class="n">straight_num</span> <span class="o">=</span> <span class="n">torch</span><span class="p">.</span><span class="n">conv1d</span><span class="p">(</span><span class="n">column_sum</span><span class="p">,</span> <span class="n">straight_filter</span><span class="p">)</span>
    <span class="n">straight_indicies</span> <span class="o">=</span> <span class="n">torch</span><span class="p">.</span><span class="n">where</span><span class="p">(</span><span class="n">straight_num</span><span class="p">.</span><span class="n">squeeze</span><span class="p">()</span> <span class="o">&gt;</span> <span class="mi">4</span><span class="p">)[</span><span class="mi">0</span><span class="p">]</span>

    <span class="n">result</span> <span class="o">=</span> <span class="p">[]</span>

    <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="n">straight_indicies</span><span class="p">:</span>
        <span class="k">if</span> <span class="n">i</span> <span class="o">==</span> <span class="mi">0</span><span class="p">:</span> <span class="c1">#in case of back straight
</span>            <span class="n">straight_sequences</span> <span class="o">=</span> <span class="n">torch</span><span class="p">.</span><span class="n">Tensor</span><span class="p">([</span><span class="o">-</span><span class="mi">1</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">3</span><span class="p">]).</span><span class="nb">long</span><span class="p">()</span>
        <span class="k">else</span><span class="p">:</span>
            <span class="n">straight_sequences</span> <span class="o">=</span> <span class="n">torch</span><span class="p">.</span><span class="n">arange</span><span class="p">(</span><span class="n">i</span><span class="o">-</span><span class="mi">1</span><span class="p">,</span> <span class="n">i</span><span class="o">+</span><span class="mi">4</span><span class="p">)</span>

        <span class="n">straight_target</span> <span class="o">=</span> <span class="n">torch</span><span class="p">.</span><span class="n">cartesian_prod</span><span class="p">(</span>
            <span class="o">*</span><span class="p">[</span><span class="n">torch</span><span class="p">.</span><span class="n">where</span><span class="p">(</span><span class="n">concat_tensor</span><span class="p">[</span><span class="mi">0</span><span class="p">,</span> <span class="p">:,</span> <span class="n">i</span><span class="o">+</span><span class="n">j</span><span class="p">])[</span><span class="mi">0</span><span class="p">]</span> <span class="k">for</span> <span class="n">j</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">5</span><span class="p">)])</span>
        
        <span class="k">for</span> <span class="n">j</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="n">straight_target</span><span class="p">)):</span>
            <span class="n">straight_tensor</span> <span class="o">=</span> <span class="n">torch</span><span class="p">.</span><span class="n">zeros_like</span><span class="p">(</span><span class="n">card_tensor</span><span class="p">)</span>
            <span class="n">straight_tensor</span><span class="p">[:,</span> <span class="n">straight_target</span><span class="p">[</span><span class="n">j</span><span class="p">],</span> <span class="n">straight_sequences</span><span class="p">]</span> <span class="o">=</span> <span class="mf">1.0</span>
            <span class="n">result</span><span class="p">.</span><span class="n">append</span><span class="p">(</span><span class="n">straight_tensor</span><span class="p">)</span>

    <span class="k">return</span> <span class="n">result</span>
</code></pre></div></div>

<p>## 스트레이트 플러시
스트레이트 플러시의 경우 스트레이트 손패행렬과 플러시 손패행렬에서 교집합을 구해주면된다. 또한 스트레이트와 플러시인 경우에서, 스트레이트인 경우를 제외 해주어야한다.</p>

<h1 id="3손패-클래스">3.손패 클래스</h1>
<p>손패 클래스는 위에서 구현한 함수를 바탕으로 플레이어의 손패를 관리해준다. 빅투에서 플레이어가 버릴 수 있는 카드의 목록들은 각 족보에 맞는 패와, 이전에 다른 플레이어가 버린 카드를 고려하여 행동이 제한되기 떄문에 손패 클래스는 특정 플레이어가 취할 수 있는 action의 목록을 알 수 있게 해주는 핵심적인 클래스이다.</p>

<p>손패 클래스를 요약하자면 특정 플레이어가 카드를 얻고, 버릴 때마다 해당 플레이어의 족보의 경우의 수를 갱신해주는 클래스이다.</p>

<h2 id="핸드-클래스의-주요-멤버">핸드 클래스의 주요 멤버</h2>
<ul>
  <li><strong><em>card_tensor: torch.Tensor</em></strong> : 플레이어가 가지고 있는 손패를 encoding한 행렬이다.</li>
  <li><strong><em>hand_rankings: Dict[Ranking, HandRanking]</em></strong> : 플레이어의 카드가 변경될 때마다, 가능한 족보를 저장해주는 dictionary이다.
    <h2 id="핸드-클래스의-주요-메소드">핸드 클래스의 주요 메소드</h2>
  </li>
  <li><strong><em>update()</em></strong> : 플레이어의 손패의 변화가 있을 때마다, <strong><em>hand_rankings</em></strong>를 갱신해주는 메소드다.</li>
  <li><strong><em>discard_cards(card_tensor: torch.Tensor)</em></strong> : 인자로 받은 카드 행렬에 해당하는 카드들을 플레이어의 손패에서 버리는 메소드이다.</li>
  <li><strong><em>get_all_hand_rankings_stronger_than(target_ranking: HandRanking)</em></strong> : 현재 손패에서 타겟 족보보다 강한 모든 족보들을 반환한다.</li>
</ul>

<h2 id="전체-코드">전체 코드</h2>
<details>
<summary>코드 보기</summary>

```python
class Hand():
    def __init__(self, card_tensor: Optional[torch.Tensor] = None):
        if card_tensor is None:
            card_tensor = torch.zeros(1, 4, 13)
        self._card_tensor = card_tensor
        self._hand_rankings: Dict[Ranking, HandRanking] = {}
        
        self.update()
    
    def get_n_card(self):
        return int(self._card_tensor.sum().item())

    
    def get_card_tensor(self):
        return self._card_tensor.clone()
    
    def update(self):
        card_tensor = self._card_tensor

        single = get_n_common_cards(card_tensor, 1)
        pair = get_n_common_cards(card_tensor, 2)
        triple = get_n_common_cards(card_tensor, 3)

        flush = get_n_common_cards(card_tensor, 5, True)
        four_card = get_n_m_common_cards(card_tensor, 4, 1)
        full_house = get_n_m_common_cards(card_tensor, 3, 2)
        straight = get_straight(card_tensor)
        
        if len(straight) == 0:
            straight_flush = []
        else:
            straight_flush_indices = torch.where(torch.max(torch.concat(straight).sum(dim=-1), dim=-1).values == 5)[0].tolist()
            straight_flush = [straight[i] for i in straight_flush_indices]
        
        if len(straight_flush) &gt; 0 and len(flush) &gt; 0:
            straight_flush_indices.reverse()
            for i in straight_flush_indices:
                del straight[i]

            concat_flush = torch.concat(flush)
            indicies = []
            for sf in straight_flush:
                indicies.append(torch.where((sf.expand_as(concat_flush) * concat_flush).view(concat_flush.size(0), -1).sum(dim=-1) == 5)[0].item())
                
            for i in sorted(indicies, reverse = True):
                del flush[i]
        
        for (ranking_tensors, ranking) in zip([None, single, pair, triple, straight, flush, full_house, four_card, straight_flush], Ranking):
            if ranking is Ranking.NONE:
                continue
            self._hand_rankings[ranking] = [HandRanking(card_tensor, ranking) for card_tensor in ranking_tensors]
            
        return self
    
    def discard_cards(self, card_tensor: torch.Tensor):
        self._card_tensor = self._card_tensor - card_tensor
        self.update()
        return self
    
    def get_all_hand_rankings(self) -&gt; List[HandRanking]:
        return  [HandRanking.PASS] + list(chain(*self._hand_rankings.values()))
    
    def get_all_hand_rankings_stronger_than(self, target_ranking: HandRanking) -&gt; List[HandRanking]:
        results = []
        for hand_ranking in self.all_hand_rankings:
            if target_ranking &lt; hand_ranking:
                results.append(hand_ranking)
        return results
    
    card_tensor = property(get_card_tensor)
    all_hand_rankings = property(get_all_hand_rankings)
    n_card = property(get_n_card)
```

</details>
<p>&lt;/br&gt;</p>

<h1 id="4요약">4.요약</h1>
<p>지금까지 손패 클래스의 구현에 대해서 알아보았다. 빅투에서 플레이어는 항상 어느 환경에서든 동일한 행동을 취할 수 있는 것이 아니라, 자신의 손패 상황에 따라서 취할 수 있는 행동이 제한되기 때문에, 해당 손패에 해당되는 족보를 알아야되며, 플레이어가 카드를 받거나 버릴 때, 가능한 족보들의 목록은 갱신되어야된다. 손패 클래스는 족보들을 명확하게 관리할 수 있게 해준다.</p>]]></content><author><name>Junhyeok Oh</name></author><category term="BigTwo" /><category term="BigTwo" /><category term="RL" /><summary type="html"><![CDATA[2.Creating Big Two environment - Hand Class]]></summary></entry><entry><title type="html">2.빅투 환경 만들기 - 족보 클래스</title><link href="https://jh2525.github.io/jh2525_.github.io/bigtwo/HandRankingClass/" rel="alternate" type="text/html" title="2.빅투 환경 만들기 - 족보 클래스" /><published>2024-02-22T00:00:00+00:00</published><updated>2024-02-22T00:00:00+00:00</updated><id>https://jh2525.github.io/jh2525_.github.io/bigtwo/HandRankingClass</id><content type="html" xml:base="https://jh2525.github.io/jh2525_.github.io/bigtwo/HandRankingClass/"><![CDATA[<h1 id="1개요">1.개요</h1>
<p>여기서는 빅투 환경에서의 사용될 족보 클래스에 대해서 설명한다. 빅투의 경우 이전 플레이어가낸 족보에 따라서 플레이어의 행동이 제한되기 때문에, 두 족보의 강함을 비교하는 것은 매우 중요하다. 따라서, 우리는 특정한 족보를 만족하는 손패 행렬이 주어졌을 때, 해당 손패 행렬을 통해서 족보 클래스를 만들어, 족보들의 강함을 비교하는데 사용할 것이다.</p>

<h1 id="2손패의-encoding">2.손패의 Encoding</h1>
<p>기본적으로 플레잉 카드는 13개의 끗수와 4개의 슈트로 구성되어있다. 또한, 빅투에서 각 슈트와 끗수의 카드의 개수는 1개로 유일하기 때문에, 우리는 카드의 있고 없음을 0(없음)과 1(있음)으로 표현이 가능하다. 따라서, (4, 13)의 크기의 행렬을 통해서 손패를 나타낼 수 있다. 따라서, 앞으로 카드들을 다음과 같은 방식으로 Encoding해서 카드들을 나타낸 것을 카드 행렬이라고하자.
<img src="https://github.com/jh2525/jh2525.github.io/assets/160830734/26b070bf-cab5-4727-928d-ce70b28f7f27" alt="encoding" /></p>

<h1 id="3족보의-일반화">3.족보의 일반화</h1>
<p>빅투에서 명시적인 족보의 강함만을 비교할 수 있다고해서, 플레이어가 할 수 있는 행동을 모두 알 수 있는 것은 아니다. 예를 들어서, 다음과 같은 상황들이 존재한다:</p>

<ul>
  <li><strong>플레이어가 첫 번째 카드를 내는 경우</strong> : 예를 들어서, 시작할 때 D3을 갖고 있는 플레이어나, 해당 플레이어를 제외한 나머지 플레이어가 연속적으로 패스를 했을 때 플레이어는 어떤 족보도 낼 수 있다. 이 때는 모든 족보의 경우가 가능하기에 족보의 비교를 통해서 낼 수 있는 카드를 알 수 없다.</li>
  <li><strong>패스</strong> : ‘패스’라는 행위 자체는 다른 족보와 비교할 수 없다.</li>
</ul>

<p>우리는 빅투 환경에서 단순히 족보들의 비교만으로 플레이어가 가능한 행동들을 알고싶은 것이기에 다음과 같은 일반화된 족보 두개를 추가한다.</p>

<ul>
  <li><strong>None</strong> : 3번의 연속적인 패스가 이루어졌거나, 플레이어가 첫 번째 플레이어 일 때, 현재 나와있는 가장 강한 족보를 None 족보로 정의한다.</li>
  <li><strong>Pass</strong> : 현재 나와 있는 가장 강한 족보가 ‘None’이 아닐 때, 플레이어가 아무 카드도 버리지 않는 것을 Pass 족보로 정의한다.</li>
</ul>

<p>이 두 족보를 통해서 우리는 플레이어가 가능한 행동을 모두 족보들의 비교를 통해서만 알 수 있게 된다. 구체적인 과정은 다음과 같다:</p>

<p>모든 None과 Pass를 포함한 가능한 족보들의 집합을 $\mathcal{R}$ 이라고하자.</p>

<p>그러면  $\mathcal{R}$에 다음과 같은 원순서(preorder)를 줄 수 있다:</p>

<p>임의의 $r, r’ \in \mathcal{R}$에 대하여, &lt;/br&gt;
(1) if $r \neq \text{Pass}, \text{None} &lt; r$, &lt;/br&gt;
(2) if $r \neq \text{None}, \ r &lt; \text{Pass}$, &lt;/br&gt;
(3) if $r, r’ \notin {\text{None, Pass}}, $ 족보 $r’$이 $r$ 보다 강하고, 버리는 카드의 개수가 같으면 $r &lt; r’$ 그리고 &lt;/br&gt;
(4) (1), (2), (3)의 경우가 아니면, $r &lt; r’$은 항상 거짓이다.</p>

<p>그러면 현재 나와있는 가장 강한 족보를 $r$ 어떤 플레이어의 손패에 대해서 모든 가능한 족보를 $\mathcal{R}_p$ 라고한다면, 단순히 플레이어가 가능한 버릴 수 있는 족보들의 집합 $\mathcal{A}_p$는 다음과 같다. $\mathcal{A}_p = {r’ \in \mathcal{R}_p \mid r &lt; r’ }$</p>

<h1 id="4족보의-강함의-수치화">4.족보의 강함의 수치화</h1>
<p>만약 어떤 두 족보가 Pass도 None도 아니고 같은 족보라면 거기서 가장 강한 카드로 족보의 강함이 결정된다. 그렇다면, 어떤 족보의 동일 족보내에서 강함은 수치화 할 수 있다. D3을 0으로 시작해서 S2를 51까지로 오름차순으로 족보내에서 강함(power)로 정의하면, 같은 족보끼리의 비교에서 유용하게 이용할 수 있다.<br />
<img src="https://github.com/jh2525/jh2525.github.io/assets/160830734/3465afb8-cf6e-424f-bd23-4c5f4537d952" alt="power_matrix" /></p>

<p>따라서 단순히, 특정 족보의 카드 행렬이 주어진다면, 위의 사진과 같이 강함 행렬(power matrix)를 정의해주고, 인덱싱을 통해서 구한 강함값 중 최댓값이 그 족보의 강함이 되는 것이다. 이를 코드로 작성하면 다음과 같다:</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">power_matrix</span> <span class="o">=</span> <span class="n">torch</span><span class="p">.</span><span class="n">arange</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="n">max_num</span> <span class="o">*</span> <span class="n">kind</span><span class="p">).</span><span class="n">view</span><span class="p">(</span><span class="n">max_num</span><span class="p">,</span> <span class="n">kind</span><span class="p">).</span><span class="n">T</span><span class="p">.</span><span class="n">roll</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="n">dims</span><span class="o">=</span><span class="mi">1</span><span class="p">)</span> 

<span class="k">def</span> <span class="nf">get_maximum_number_power</span><span class="p">(</span><span class="n">hand_tensor</span><span class="p">:</span> <span class="n">torch</span><span class="p">.</span><span class="n">Tensor</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="nb">int</span><span class="p">:</span>
    <span class="s">"""
    인자로 주어진 손패 행렬에 대해서, 가장 강한 끗수와 슈트를 대소가 바뀌지 않는 숫자로 반환합니다.
    """</span>
    <span class="k">if</span> <span class="mi">3</span> <span class="o">!=</span> <span class="n">hand_tensor</span><span class="p">.</span><span class="n">dim</span><span class="p">():</span>
        <span class="k">raise</span> <span class="nb">ValueError</span>
    <span class="n">_</span><span class="p">,</span> <span class="n">suits</span><span class="p">,</span> <span class="n">numbers</span> <span class="o">=</span> <span class="n">torch</span><span class="p">.</span><span class="n">where</span><span class="p">(</span><span class="n">hand_tensor</span><span class="p">)</span>
    <span class="k">return</span> <span class="n">power_matrix</span><span class="p">[</span><span class="n">suits</span><span class="p">,</span> <span class="n">numbers</span><span class="p">].</span><span class="nb">max</span><span class="p">().</span><span class="n">item</span><span class="p">()</span>
</code></pre></div></div>

<h1 id="5족보-클래스">5.족보 클래스</h1>

<p><strong>3.족보의 일반화</strong>에 나왓듯이 이제 족보의 강함을 비교할 수 있는 클래스는 단순히 어떤 손패 행렬을 받았을 때, 해당 손패 행렬과, 해당 손패 행렬에 해당하는 족보의 이름을 가진 클래스와 가장 높은 끗수를 멤버로 구성하고, ‘&lt;’ 연산자를 오버로딩만 해주면 쉽게 구현할 수 있다. 족보 클래스 구현 코드는 길지 않으므로 주석과 함께 코드를 작성해보면 다음과 같다.</p>

<h2 id="족보의-열거형-데이터">족보의 열거형 데이터</h2>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">class</span> <span class="nc">Ranking</span><span class="p">(</span><span class="n">Enum</span><span class="p">):</span>
    <span class="n">NONE</span> <span class="o">=</span> <span class="mi">0</span>
    <span class="n">SINGLE</span> <span class="o">=</span> <span class="mi">1</span>
    <span class="n">PAIR</span> <span class="o">=</span> <span class="mi">2</span>
    <span class="n">TRIPLE</span> <span class="o">=</span> <span class="mi">3</span>
    <span class="n">STRAIGHT</span> <span class="o">=</span> <span class="mi">4</span>
    <span class="n">FLUSH</span> <span class="o">=</span> <span class="mi">5</span>
    <span class="n">FULL_HOUSE</span> <span class="o">=</span> <span class="mi">6</span>
    <span class="n">FOUR_CARD</span> <span class="o">=</span> <span class="mi">7</span>
    <span class="n">STRAIGHT_FLUSH</span> <span class="o">=</span> <span class="mi">8</span>
    <span class="n">PASS</span> <span class="o">=</span> <span class="mi">9</span>
</code></pre></div></div>

<h2 id="족보-클래스">족보 클래스</h2>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">get_maximum_number_power</span><span class="p">(</span><span class="n">hand_tensor</span><span class="p">:</span> <span class="n">torch</span><span class="p">.</span><span class="n">Tensor</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="nb">float</span><span class="p">:</span>
    <span class="s">"""
    인자로 주어진 손패 행렬에 대해서, 가장 강한 끗수와 슈트를 대소가 바뀌지 않는 숫자로 반환합니다.
    """</span>
    <span class="k">if</span> <span class="mi">3</span> <span class="o">!=</span> <span class="n">hand_tensor</span><span class="p">.</span><span class="n">dim</span><span class="p">():</span>
        <span class="k">raise</span> <span class="nb">ValueError</span>
    <span class="n">_</span><span class="p">,</span> <span class="n">suits</span><span class="p">,</span> <span class="n">numbers</span> <span class="o">=</span> <span class="n">torch</span><span class="p">.</span><span class="n">where</span><span class="p">(</span><span class="n">hand_tensor</span><span class="p">)</span>
    <span class="k">return</span> <span class="n">power_matrix</span><span class="p">[</span><span class="n">suits</span><span class="p">,</span> <span class="n">numbers</span><span class="p">].</span><span class="nb">max</span><span class="p">().</span><span class="n">item</span><span class="p">()</span>


<span class="k">class</span> <span class="nc">HandRanking</span><span class="p">:</span>

    <span class="k">def</span> <span class="nf">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">card_tensor</span><span class="p">:</span> <span class="n">torch</span><span class="p">.</span><span class="n">Tensor</span><span class="p">,</span> <span class="n">ranking</span><span class="p">:</span> <span class="n">Ranking</span><span class="p">):</span>
        <span class="bp">self</span><span class="p">.</span><span class="n">_card_tensor</span> <span class="o">=</span> <span class="n">card_tensor</span> <span class="c1">#손패 행렬
</span>        <span class="bp">self</span><span class="p">.</span><span class="n">_ranking</span> <span class="o">=</span> <span class="n">ranking</span> <span class="c1">#족보 이름
</span>        <span class="k">if</span> <span class="n">ranking</span> <span class="ow">in</span> <span class="p">[</span><span class="n">Ranking</span><span class="p">.</span><span class="n">SINGLE</span><span class="p">,</span> <span class="n">Ranking</span><span class="p">.</span><span class="n">PAIR</span><span class="p">,</span> <span class="n">Ranking</span><span class="p">.</span><span class="n">TRIPLE</span><span class="p">,</span> <span class="n">Ranking</span><span class="p">.</span><span class="n">STRAIGHT</span><span class="p">,</span> <span class="n">Ranking</span><span class="p">.</span><span class="n">STRAIGHT_FLUSH</span><span class="p">,</span> <span class="n">Ranking</span><span class="p">.</span><span class="n">FLUSH</span><span class="p">]:</span>
            <span class="bp">self</span><span class="p">.</span><span class="n">power</span> <span class="o">=</span> <span class="n">get_maximum_number_power</span><span class="p">(</span><span class="n">card_tensor</span><span class="p">)</span> <span class="c1">#싱글, 페어, 트리플, 스트레이트, 스트레이트 플러시인 경우는 단순히 가장 강한 끗수와 슈트를 비교하기만 하면된다.
</span>        <span class="k">elif</span> <span class="n">ranking</span> <span class="ow">in</span> <span class="p">[</span><span class="n">Ranking</span><span class="p">.</span><span class="n">FULL_HOUSE</span><span class="p">,</span> <span class="n">Ranking</span><span class="p">.</span><span class="n">FOUR_CARD</span><span class="p">]:</span> <span class="c1">#풀하우스나, 포카드인 경우는 가장 많은 사용한 끗수의 가장 강한 슈트와 끗수를 비교해야한다.
</span>            <span class="n">target</span> <span class="o">=</span> <span class="n">torch</span><span class="p">.</span><span class="n">zeros_like</span><span class="p">(</span><span class="n">card_tensor</span><span class="p">)</span>
            <span class="n">target_indicies</span> <span class="o">=</span> <span class="n">card_tensor</span><span class="p">.</span><span class="nb">sum</span><span class="p">(</span><span class="n">dim</span><span class="o">=</span><span class="mi">1</span><span class="p">).</span><span class="n">argmax</span><span class="p">()</span>
            <span class="n">target</span><span class="p">[:,</span> <span class="p">:,</span> <span class="n">target_indicies</span><span class="p">]</span> <span class="o">=</span> <span class="n">card_tensor</span><span class="p">[:,</span> <span class="p">:,</span> <span class="n">target_indicies</span><span class="p">]</span> <span class="c1">#가장 많이 사용한 끗수의 카드들만 1로 만들어서 족보를 구성하는 가장 강한 끗수와 슈트를 구한다.
</span>            <span class="bp">self</span><span class="p">.</span><span class="n">power</span> <span class="o">=</span> <span class="n">get_maximum_number_power</span><span class="p">(</span><span class="n">target</span><span class="p">)</span>
        <span class="k">else</span><span class="p">:</span>
            <span class="bp">self</span><span class="p">.</span><span class="n">power</span> <span class="o">=</span> <span class="mi">0</span> <span class="c1">#None 또는 Pass인 경우는 항상 power가 0으로 정의한다.
</span>
    <span class="k">def</span> <span class="nf">__lt__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">other</span><span class="p">):</span>
        <span class="n">ranking_value</span> <span class="o">=</span> <span class="bp">self</span><span class="p">.</span><span class="n">_ranking</span><span class="p">.</span><span class="n">value</span>
        <span class="k">if</span> <span class="bp">self</span><span class="p">.</span><span class="n">_ranking</span> <span class="o">==</span> <span class="n">Ranking</span><span class="p">.</span><span class="n">NONE</span><span class="p">:</span>
            <span class="k">return</span> <span class="p">(</span><span class="n">other</span><span class="p">.</span><span class="n">ranking</span> <span class="o">!=</span> <span class="n">Ranking</span><span class="p">.</span><span class="n">PASS</span><span class="p">)</span>
        <span class="k">elif</span> <span class="n">other</span><span class="p">.</span><span class="n">ranking</span> <span class="o">==</span> <span class="n">Ranking</span><span class="p">.</span><span class="n">PASS</span><span class="p">:</span>
            <span class="k">return</span> <span class="bp">True</span>
        <span class="k">elif</span> <span class="n">ranking_value</span> <span class="o">&lt;=</span> <span class="mi">3</span><span class="p">:</span>
            <span class="k">return</span> <span class="p">(</span><span class="n">ranking_value</span> <span class="o">==</span> <span class="n">other</span><span class="p">.</span><span class="n">ranking</span><span class="p">.</span><span class="n">value</span><span class="p">)</span> <span class="ow">and</span> <span class="p">(</span><span class="bp">self</span><span class="p">.</span><span class="n">power</span> <span class="o">&lt;</span> <span class="n">other</span><span class="p">.</span><span class="n">power</span><span class="p">)</span>
        <span class="k">else</span><span class="p">:</span>
            <span class="k">if</span> <span class="n">ranking_value</span> <span class="o">&lt;</span> <span class="n">other</span><span class="p">.</span><span class="n">ranking</span><span class="p">.</span><span class="n">value</span><span class="p">:</span>
                <span class="k">return</span> <span class="bp">True</span>
            <span class="k">else</span><span class="p">:</span>
                <span class="k">return</span> <span class="p">(</span><span class="n">ranking_value</span> <span class="o">==</span> <span class="n">other</span><span class="p">.</span><span class="n">ranking</span><span class="p">.</span><span class="n">value</span><span class="p">)</span> <span class="ow">and</span> <span class="p">(</span><span class="bp">self</span><span class="p">.</span><span class="n">power</span> <span class="o">&lt;</span> <span class="n">other</span><span class="p">.</span><span class="n">power</span><span class="p">)</span>

    <span class="k">def</span> <span class="nf">get_card_tensor</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
        <span class="k">return</span> <span class="bp">self</span><span class="p">.</span><span class="n">_card_tensor</span><span class="p">.</span><span class="n">clone</span><span class="p">()</span>
    
    <span class="k">def</span> <span class="nf">get_ranking</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
        <span class="k">return</span> <span class="bp">self</span><span class="p">.</span><span class="n">_ranking</span>
    
    <span class="n">card_tensor</span> <span class="o">=</span> <span class="nb">property</span><span class="p">(</span><span class="n">get_card_tensor</span><span class="p">)</span>
    <span class="n">ranking</span> <span class="o">=</span> <span class="nb">property</span><span class="p">(</span><span class="n">get_ranking</span><span class="p">)</span>

<span class="n">HandRanking</span><span class="p">.</span><span class="n">PASS</span> <span class="o">=</span> <span class="n">HandRanking</span><span class="p">(</span><span class="n">torch</span><span class="p">.</span><span class="n">zeros</span><span class="p">((</span><span class="mi">1</span><span class="p">,</span> <span class="mi">4</span><span class="p">,</span> <span class="mi">13</span><span class="p">)),</span> <span class="n">Ranking</span><span class="p">.</span><span class="n">PASS</span><span class="p">)</span> <span class="c1">#Pass 족보는 항상 똑같기 때문에 정적 변수로 생성해준다
</span><span class="n">HandRanking</span><span class="p">.</span><span class="n">NONE</span> <span class="o">=</span> <span class="n">HandRanking</span><span class="p">(</span><span class="n">torch</span><span class="p">.</span><span class="n">zeros</span><span class="p">((</span><span class="mi">1</span><span class="p">,</span> <span class="mi">4</span><span class="p">,</span> <span class="mi">13</span><span class="p">)),</span> <span class="n">Ranking</span><span class="p">.</span><span class="n">NONE</span><span class="p">)</span> <span class="c1">#None 족보도 마찬가지
</span></code></pre></div></div>]]></content><author><name>Junhyeok Oh</name></author><category term="BigTwo" /><category term="BigTwo" /><category term="RL" /><summary type="html"><![CDATA[2.Creating Big Two environment - Hand Ranking Class]]></summary></entry><entry><title type="html">1.빅투의 개요</title><link href="https://jh2525.github.io/jh2525_.github.io/bigtwo/The_introduction_of_Big_Two/" rel="alternate" type="text/html" title="1.빅투의 개요" /><published>2024-02-22T00:00:00+00:00</published><updated>2024-02-22T00:00:00+00:00</updated><id>https://jh2525.github.io/jh2525_.github.io/bigtwo/The_introduction_of_Big_Two</id><content type="html" xml:base="https://jh2525.github.io/jh2525_.github.io/bigtwo/The_introduction_of_Big_Two/"><![CDATA[<h1 id="1빅투big-two란">1.빅투(Big Two)란?</h1>

<p>빅투란 플레잉 카드를 사용해서 진행하는 게임으로써, 간단히 설명하자면 각 플레이어가 자신의 손패를 가장 먼저 전부 털어 놓으면 이기는 게임이다.</p>

<h2 id="1게임-진행">1.게임 진행</h2>

<h3 id="--플레이어가-해당-라운드의-첫-번쨰-플레이어인-경우">- 플레이어가 해당 라운드의 첫 번쨰 플레이어인 경우</h3>

<p>족보와 일치하는 카드의 조합을 손패에서 내려놓는다. 해당 게임에서의 최초의 플레이어는 3다이아몬드를 가진 플레이어다. 그 이후는 마지막 카드를 버린 플레이어를 제외하고 연속적으로 모두 패스를 했을 시, 마지막으로 카드를 버린 플레이어가 첫 번째 플레이어로 라운드를 다시 시작한다.</p>

<h3 id="--플레이어가-해당-라운드의-첫-번째-플레이어가-아닌-경우">- 플레이어가 해당 라운드의 첫 번째 플레이어가 아닌 경우</h3>

<p>플레이어는 카드를 버릴지 아니면, 패스를 할 지 선택을 한다. 플레이어는 다음을 만족하는 카드의 조합만 버릴 수 있다:</p>

<ul>
  <li>해당 라운드의 첫 번째 플레이어가 낸 족보의 카드의 수와 동일한 수의 카드를 버려야 된다. 예를 들어서, 첫 번째 플레이어가 두 장의 카드를 버렸다면, 모든 플레이어는 해당 라운드에서 한 번에 두 장의 카드만 버릴 수 있다.</li>
  <li>플레이어는 이전에 카드를 버린 사람의 조합보다 더 강한 조합의 카드를 버려야 된다.</li>
</ul>

<h3 id="--게임-종료-조건">- 게임 종료 조건</h3>
<p>만약 특정 플레이어가 손패를 버린 후 남아있는 손패가 없다면 해당 게임은 종료된다.</p>

<h2 id="2족보">2.족보</h2>

<p>번거로움을 피하기 위해, 앞으로 해당 프로젝트에서 카드의 조합을 표기할 때는 다음과 같은 방법으로 표기하자.
대문자 알파벳은 슈트를 숫자와 소문자 알파벳은 끗수를 나타낸다. 예를 들어서, D 34 S 5j는 다이아 3, 4 스페이드 5, J를 나타낸다.</p>

<h3 id="1-싱글">(1) 싱글</h3>

<p>1장의 카드로 이루어진 조합이다. <strong>예를 들어서, D3은 싱글이다.</strong></p>
<h3 id="2-페어">(2) 페어</h3>

<p>동일한 끗수의 2장의 카드로 이루어진 조합이다. <strong>예를 들어서, D2 S2는 페어다.</strong></p>

<h3 id="3-트리플">(3) 트리플</h3>

<p>동일한 끗수의 3장의 카드로 이루어진 조합이다. <strong>예를 들어서, Dj Cj Hj는 트리플이다.</strong></p>

<h3 id="4-스트레이트">(4) 스트레이트</h3>

<p>연속적인 끗수 5장의 카드로 이루어진 조합이다. <strong>예를 들어서, D25 H346은 스트레이트다.</strong> &lt;/br&gt;또한, 백스트레이트(a23456)를 제외한 모든 스트레이트는 연속한 끗수여야된다. 즉, jqka2는 스트레이트가 아니다.</p>

<h3 id="5-플러시">(5) 플러시</h3>

<p>같은 슈트 5장의 카드로 이루어진 조합이다. <strong>예를 들어서, D457910은 플러시이다.</strong></p>

<h3 id="6-풀하우스">(6) 풀하우스</h3>

<p>같은 끗수 3장 그리고 같은 끗수 2장의 카드로 이루어진 조합이다. <strong>예를 들어서, D35 C3 H35는 풀하우스이다.</strong></p>

<h3 id="7-포카드">(7) 포카드</h3>

<p>같은 끗수 4장 그리고 아무 카드 1장으로 이루어진 조합이다. <strong>예를 들어서, D3j Cj Hj Sj는 포카드이다.</strong></p>

<h3 id="8-스트레이트-플러시">(8) 스트레이트 플러시</h3>

<p>플러시이면서 스트레이트인 카드의 조합이다. <strong>예를 들어서, S 45678은 스트레이트 플러시이다.</strong></p>

<h2 id="3카드의-조합의-강함">3.카드의 조합의 강함</h2>

<p>기본적으로 모든 조합의 경우 끗수가 같은 경우는 슈트의 강함으로 어느 조합이 더 강한지 결정된다.&lt;/br&gt; 다이아몬드 &lt; 클로버 &lt; 하트 &lt; 스페이드 순으로 강하다. 끗수의 강함은 3 4 5 6 7 8 9 10 j q k a 2 순이다. (오른쪽에 있을 수록 강한 끝수이다.)</p>

<h3 id="--동일한-족보인-경우">- 동일한 족보인 경우</h3>

<h4 id="스트레이트-플러시">스트레이트 (플러시)</h4>
<p>백스트레이트가 가장 강하며, 그 다음으로는 강한 끗수가 많은 스트레이트가 더 강한 스트레이트다.</p>

<h4 id="풀하우스">풀하우스</h4>
<p>동일한 끗수 3개의 끗수가 강할수록 강한 풀하우스이다.</p>

<h4 id="포카드">포카드</h4>
<p>동일한 끗수 4개의 끗수가 강할수록 강한 포카드이다.</p>

<h4 id="그-이외의-족보">그 이외의 족보</h4>
<p>해당 조합을 이루는 카드 중에서 가장 강한 끗수로 강함을 비교한다. 만약 가장 강한 끗수가 동일하다면, 가장 강한 끗수의 슈트를 비교한다.</p>

<h3 id="--다른-족보인-경우">- 다른 족보인 경우</h3>
<p>스트레이트, 플러시, 풀하우스, 포카드, 스트레이트 플러시 순으로 강하다.</p>

<h2 id="4강화학습-시-예상되는-어려움">4.강화학습 시 예상되는 어려움</h2>
<p>빅투는 바둑과 체스와 같은 보드게임과 달리 상대의 패를 알 수 없는 불완전정보 게임이다. 플레이어가 관측한 상태가 이전 상태와 동일한 상태라고해도, 특정 행동이 항상 고정된 정책을 가진 플레이어 상대로도 똑같은 결과를 얻을 수 없음을 의미한다. 따라서 이러한 무작위성은 학습의 안정성에 악영향을 끼칠 것이며, 이러한 불안정성을 잘 다루는 것이 관건이 될 것이라고 예상한다. 또한, 족보의 경우의 수가 매우 많기 때문에 에이전트의 액션을 어떻게 구현할지도 한가지 중요한 안건이 될 것이다.</p>

<h2 id="용어-설명">용어 설명</h2>

<ul>
  <li>
    <p>라운드(trick) : 첫 번째 플레이어가 카드를 낸 후, 게임이 종료되거나, 한 플레이어를 제외하고 나머지 플레이어들이 연속적으로 패스를 할 때까지를 라운드라고한다. 공식용어는 아니다.</p>
  </li>
  <li>
    <p>슈트(suit) : 스페이드(♠), 하트(♥), 클로버(♣), 다이아몬드(♦)와 같은 플레잉 카드에 있는 문양을 말한다.</p>
  </li>
  <li>
    <p>끗수(number) : 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, j, q, k, a와 같이 플레잉 카드에 있는 숫자와 알파벳을 말한다.</p>
  </li>
</ul>

<h2 id="참고-문헌">참고 문헌</h2>

<ul>
  <li>Big two, Wikipedia, https://en.wikipedia.org/wiki/Big_two</li>
  <li>빅투, 나무위키, https://namu.wiki/w/%EB%B9%85%ED%88%AC</li>
</ul>]]></content><author><name>Junhyeok Oh</name></author><category term="BigTwo" /><category term="BigTwo" /><category term="RL" /><summary type="html"><![CDATA[The introduction of Big Two]]></summary></entry></feed>