ブラックジャックもどきゲーム、BlackJick

カードゲームのブラックジャックをコンピュータとプレイするプログラム（もどき）を作ったけれども、カジノでやるようなブラックジャックは知らないし、指摘を受けて、また作り直そうとしてwebを検索してみたら、ディーラーとのやり取りが意外と複雑だったので、開発を中止し、元のプログラムはせっかく作ったし、これはこれで遊べるので、'BlackJick'として残しておきます。Jickとは、僕が以前飼ってた黒猫で、隣の幼稚園の壁の上に置いてコンビニに出かけたら、いなくなりました。このプログラムをJickに捧げます。

Cで書かれています。

ソースをコピペして、`cc blackjick.c -o blackjick`としてコンパイルし、`./blackjick`として実行させてください。

このプログラムは古典的確率論で推測する初歩的人工知能版です。


### 遊び方

最初にコンピュータ、プレイヤー双方ともに2枚のカードが配られます。相手のカードは見えません。手札の合計が21を超えないように山からカードを引いていきます。コンピュータは確率計算に基づきカードを引いていきます。
ジョーカーは1枚入っていて、0から10までの任意の値を取ることができます。Ａ（エース)は常に１です。ブラックジャックのように11になったりしません。Jack,Queen,Kingは共に10として計算します。手札の合計が大きいほうが勝ちですが、21を超えたらアウトです。コンピュータ、プレイヤーが同じ点数の場合、ジョーカーを持ってたほうが勝ちです。
1.でカードを引き、2.でコンピュータと勝負です。

```blackjick.c
#include	<stdio.h>
#include	<stdlib.h>
#include	<time.h>

typedef struct {
    int mark; // 0:no card 1:heart 2:dia 3:club 4:spade 5:joker
    int number; // 1 ~ 13 0:joker
} card;

#define TRUMP (53)
#define DRAWMAX (12)    		// maximum number of cards per player

card cards[TRUMP];
int cardidx=0;
card comp_cards[DRAWMAX];
card player_cards[DRAWMAX];

card id2card(int c)
{
    card cd;
    cd.mark=(c==0)?5:(c-1)/13+1;
    cd.number=(c==0)?0:(c-1)%13+1;
    return cd;
}

void shuffle()
{
    int r[TRUMP]= {0},ss[TRUMP],c;
    srand((unsigned)time(NULL));
    for(int k=0; k<TRUMP; k++) {
        while(r[c=rand()%TRUMP]);
        r[ss[k]=c]=1;
    }
    for(int i=0; i<TRUMP; i++)
        cards[i]=id2card(ss[i]);
}

void draw_card(card cd[])
{
    int i=0;
    while(cd[i].mark) i++;
    if (cardidx<TRUMP && i<DRAWMAX)
        cd[i]=cards[cardidx++];
}

void init_players()
{
    for(int i=0; i<DRAWMAX; i++)
        comp_cards[i].mark=player_cards[i].mark=0;
    draw_card(comp_cards);
    draw_card(comp_cards);
    draw_card(player_cards);
    draw_card(player_cards);
}

int number_of_cards(card cd[])
{
    int idx=0;
    while(cd[idx].mark) idx++;
    return idx;
}

int isjoker(card cd[])
{
    int idx=0;
    while(cd[idx].mark)
        if (cd[idx++].mark==5)
            return 1;
    return 0;
}

void printcard(card cd)
{
    static char *marks[]= {"","♥","♦","♣","♠","Joker"};
    static char *numbers[]= {"","A","2","3","4","5","6","7","8","9","10",
                             "J","Q","K" };
    printf("[%s%s] ",marks[cd.mark],numbers[cd.number]);
}

int count_cards(card cd[])
{
    int n,i=0,v=0;
    while(cd[i].mark) {
        n=cd[i++].number;
        v+=n<=10?n:10;
    }
    return v;
}

int eval_cards(card cd[])
{
    int v=count_cards(cd),jv=isjoker(cd)?(v<=21?(21-v>=11?10:21-v):0):0;
    return v+jv;
}

void print_players_line(card cd[])
{
    int t,i=0;
    while(cd[i].mark)
        printcard(cd[i++]);
    printf(" total=%3d ",t=eval_cards(cd));
    if (t==21)
        printf("*BlackJick*");
    printf("\n");
}

void print_comp()
{
    int idx=0;
    while(comp_cards[idx++].mark)
        printf("[###] ");
    printf("%d cards.\n",idx-1);
}

int game()
{
    // game
    int status,cv=eval_cards(comp_cards),pv=eval_cards(player_cards);
    if (cv==21 && pv==21|| cv==pv)
        if (isjoker(comp_cards))
            status=2;
        else if (isjoker(player_cards))
            status=1;
        else
            status=0;
    else if (cv>=22 && pv>=22)
        status=0;
    else if (cv>=22 && pv<=21)
        status=1;
    else if (cv<=21 && pv>=22)
        status=2;
    else if (cv<pv)
        status=1;
    else
        status=2;
    return status;
}

double expected_value_of_next_card(card self[])
{
    int noc=TRUMP-number_of_cards(self),n=count_cards(self);
    int j=isjoker(self)?0:(n>=21?0:(n<11?10:21-n));
    return ((double)340+j-n)/noc;
}

int todrawp(card self[])
{
    return eval_cards(self)>=21?0:(count_cards(self)+expected_value_of_next_card(self))<=21.00?1:0;
}

int computer_turn()
{
    if (todrawp(comp_cards)) {
        printf("Computer drew a card.\n");
        draw_card(comp_cards);
        return 1;
    }
    return 0;
}

void play()
{
    int d=0;
    do {
        double e;
        printf("---------------------------------------------------\n");
        printf("Pile : %d cards\n",TRUMP-cardidx);
        printf("Computer's cards : ");
        print_comp();
        printf("Player's cards : ");
        print_players_line(player_cards);
        e=expected_value_of_next_card(player_cards);
        printf("Expected value of next 1 card is %1.8lf.\n",e);
        printf("Have to draw ? %s.\n",todrawp(player_cards)?"Yes":"No");
        printf("1. Draw a card.\n2. Game.\n: ");
        scanf("%d",&d);
        if (d==1)
            draw_card(player_cards);
        computer_turn();
    } while(d!=2);
    while(computer_turn());
}

void result(int g) {
    printf("====================Result====================\n");
    printf("Computer : ");
    print_players_line(comp_cards);
    printf("Player : ");
    print_players_line(player_cards);
    printf("%s.\n",(g==0)?"Draw game":(g==1)?"Player win":"Computer win");
}

int main()
{
    printf("**********BlackJick ver 1.0**********\n");
    shuffle();
    init_players();
    play();
    result(game());
}

``` 

##### python版

```blackjick.py
import random

class Card:
    def __init__(self, mark, number):
        self.mark = mark  # 0:no card 1:heart 2:dia 3:club 4:spade 5:joker
        self.number = number  # 1 ~ 13 0:joker

trump = 53
drawmax = 12
cards = []
cardidx = 0
comp_cards = [Card(0, 0) for _ in range(drawmax)]
player_cards = [Card(0, 0) for _ in range(drawmax)]

def id2card(c):
    mark = 5 if c == 0 else (c - 1) // 13 + 1
    number = 0 if c == 0 else (c - 1) % 13 + 1
    return Card(mark, number)

def shuffle():
    global cards, cardidx
    r = [0] * trump
    ss = []
    random.seed()
    for _ in range(trump):
        c = random.randint(0, trump-1)
        while r[c]:
            c = random.randint(0, trump-1)
        r[c] = 1
        ss.append(c)
    cards = [id2card(ss[i]) for i in range(trump)]

def draw_card(cd):
    global cardidx
    i = 0
    while cd[i].mark:
        i += 1
    if cardidx < trump and i < drawmax:
        cd[i] = cards[cardidx]
        cardidx += 1

def init_players():
    global comp_cards, player_cards
    for i in range(drawmax):
        comp_cards[i].mark = player_cards[i].mark = 0
    draw_card(comp_cards)
    draw_card(comp_cards)
    draw_card(player_cards)
    draw_card(player_cards)

def number_of_cards(cd):
    idx = 0
    while cd[idx].mark:
        idx += 1
    return idx

def isjoker(cd):
    idx = 0
    while cd[idx].mark:
        if cd[idx].mark == 5:
            return 1
        idx += 1
    return 0

def printcard(cd):
    marks = ["", "♥", "♦", "♣", "♠", "Joker"]
    numbers = ["", "A", "2", "3", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K"]
    print("[{}{}] ".format(marks[cd.mark], numbers[cd.number]), end='')

def count_cards(cd):
    i = 0
    total_value = 0
    while cd[i].mark:
        n = cd[i].number
        total_value += n if n <= 10 else 10
        i += 1
    return total_value

def eval_cards(cd):
    total_value = count_cards(cd)
    joker_value = 10 if isjoker(cd) and total_value <= 21 else 0
    return total_value + joker_value

def print_players_line(cd):
    i = 0
    while cd[i].mark:
        printcard(cd[i])
        i += 1
    print("total={:3d} ".format(eval_cards(cd)), end='')
    if eval_cards(cd) == 21:
        print("*BlackJick*")
    print()

def print_comp():
    idx = 0
    while comp_cards[idx].mark:
        print("[###] ", end='')
        idx += 1
    print("{} cards.".format(idx - 1))

def game():
    cv = eval_cards(comp_cards)
    pv = eval_cards(player_cards)
    if (cv == 21 and pv == 21) or cv == pv:
        if isjoker(comp_cards):
            return 2
        elif isjoker(player_cards):
            return 1
        else:
            return 0
    elif cv >= 22 and pv >= 22:
        return 0
    elif cv >= 22 and pv <= 21:
        return 1
    elif cv <= 21 and pv >= 22:
        return 2
    elif cv < pv:
        return 1
    else:
        return 2

def expected_value_of_next_card(self):
    noc = trump - number_of_cards(self)
    n = count_cards(self)
    j = 0 if isjoker(self) else 0 if n >= 21 else 10 if n < 11 else 21 - n
    return (340 + j - n) / noc

def todrawp(self):
    return 0 if eval_cards(self) >= 21 else 1 if count_cards(self) + expected_value_of_next_card(self) <= 21.0 else 0

def computer_turn():
    global comp_cards
    if todrawp(comp_cards):
        print("Computer drew a card.")
        draw_card(comp_cards)
        return 1
    return 0

def play():
    d = 0
    while d != 2:
        print("---------------------------------------------------")
        print(f"Pile : {trump - cardidx} cards")
        print("Computer's cards : ", end='')
        print_comp()
        print("Player's cards : ", end='')
        print_players_line(player_cards)
        e = expected_value_of_next_card(player_cards)
        print(f"Expected value of next 1 card is {e:.8f}.")
        print("Have to draw ? {}.".format("Yes" if todrawp(player_cards) else "No"))
        d = int(input("1. Draw a card.\n2. Game.\n: "))
        if d == 1:
            draw_card(player_cards)
        computer_turn()
    while computer_turn():
        pass

def result(g):
    print("====================Result====================")
    print("Computer : ", end='')
    print_players_line(comp_cards)
    print("Player : ", end='')
    print_players_line(player_cards)
    print(["Draw game", "Player win", "Computer win"][g])

if __name__ == "__main__":
    print("**********BlackJick ver 1.0**********")
    shuffle()
    init_players()
    play()
    result(game())
```