Python:麻雀対戦組み合わせ作成(汎用化済)

Python

目標:四人麻雀22人の対戦組み合わせ

四人麻雀で22に人、5卓、毎回二人休みとして、11回戦の麻雀大会をやることになり、組み合わせ表プログラムをpythonで組んでみました。当初、誰かとの対戦回数を完全に2回以下に抑えるのを目指しましたが、一筋縄ではいかない。あまり時間かけても仕方ないと割り切り、ランダム10万回ループまでは対戦2回以下として、10万回超えて時間がかかるようになったら、対戦3回を許容する形にしてみました。完璧な組み合わせを作る必要もないので、コスパとしてはいい方かと。またこれくらい簡単な方がいろいろなバリエーションに対応しやすいですね。

【汎用化済】22人、5卓、2人休み以外にも対応しました。

Pythonコード

import random
import numpy as np

random.seed(1234)

NUM_PLAYERS = 22 # 人数
NUM_TABLES = 5 # 並列に対戦する卓の数
PERSON_TABLE = 4 # 4人卓(固定)
NUM_REST = NUM_PLAYERS - NUM_TABLES * PERSON_TABLE # 一度に休む人の数
NUM_ROUND = 11 # 対戦回数
NUM_THSHOULD = 3 # 1 vs 1 で見たときの最大対戦回数

print(f"四人麻雀{NUM_PLAYERS}人・{NUM_TABLES}卓・{NUM_REST}人休み:組み合わせ")
print(f"{NUM_ROUND}回戦、同じ人との対戦は{NUM_ROUND}回以内")

if NUM_PLAYERS < NUM_TABLES * PERSON_TABLE:
    print(f'エラー:プレイヤー{NUM_PLAYERS}人は{NUM_TABLES}卓×{PERSON_TABLE}人よりすくないです')
    quit()

players = list(range(NUM_PLAYERS))
print('players:', players)

# 休む人のパターン作成
rest = []
for i in range(NUM_ROUND):
    l = []
    for j in range(NUM_REST):
        l.append(players[(i*NUM_REST + j)%NUM_PLAYERS])
    rest.append(l)
print(f'{NUM_ROUND}回線中、休む人のパターン', rest)

def check_max_match(match, candidate) :
    m = 0
    for j in range(int(len(candidate)/4)):
        d = candidate[j*PERSON_TABLE:j*PERSON_TABLE+PERSON_TABLE]
        for x in range(PERSON_TABLE):
            for y in range(PERSON_TABLE):
                if x == y :
                    continue
                m = max(m, match[d[x]][d[y]] + 1)
    return m

def update_match(match, candidate) :
    for j in range(int(len(candidate)/4)):
        # 4人卓ごとに切り出し
        d = candidate[j*PERSON_TABLE:j*PERSON_TABLE+PERSON_TABLE]
        # 4人卓の二人ペアごとにmatch回数をupdate
        for x in range(PERSON_TABLE):
            for y in range(PERSON_TABLE):
                if x == y :
                    continue
                match[d[x]][d[y]] += 1
    return match

def count_match(match) :
    nar = np.zeros(NUM_ROUND, dtype=int)
    for x in range(len(match)):
        for y in range(len(match[x])):
            if x >= y:
                continue
            nar[match[x][y]] += 1
    return nar

def create_candidate(playsers, rest, loop) :
    candidate = list(set(players) - set(rest[loop]))
    random.shuffle(candidate)
    return candidate

#================
r = []
n_retry = 0
match_th = 1 # 極力同じ人との対戦回数をこの数字以下にするLOOP_MAXまでretryしたら +1
LOOP_MAX = 100000 # 時間がかかりすぎるので、このretry回数以上になると match_thを緩める

match = np.zeros((len(players), len(players)), dtype=int)
for i in range(NUM_ROUND):
    random_loop = True
    loop_c = 0
    while random_loop :
        if n_retry > LOOP_MAX:
            match_th += 1
            n_retry = 0
        #print('***', loop_c)
        candidate = create_candidate(players, rest, i)
        m = check_max_match(match, candidate)
        #print('m:', m)
        if m > match_th :
            n_retry += 1
        else:
            match = update_match(match, candidate)
            random_loop = False
        loop_c += 1
    r.append(candidate)
    n_match = count_match(match)
    print(f'Done! loop:{i}, match max: {match_th}, Retry: {n_retry}, Remaining ', end='')
    for i in range(match_th):
        print(f'{i}:{n_match[i]} ', end='')
    print('')

#================
print(f'マッチ回数表:{match_th}回以下')
#print(match)
for x in range(len(match)):
    for y in range(len(match[x])):
        if x >= y:
            print('  ', end='')
        else:
            print(f'{match[x][y]} ', end='')
    print('')

#================
print('対戦組み合わせ')
for i in range(len(r)): # 11回戦ループ
    print(f'{i+1:02}回', end='')
    for j in range(len(r[i])): # 20人ループ
        if not j % 4:
            print('|', end='')
        else:
            print(' ', end='')
        print(f'{r[i][j]:02}', end='')
    print('|', end='')
    if NUM_REST:
        print(f'休|', end='')
        for j in range(NUM_REST):
                print(f'{rest[i][j]:02}', end='')
                if j != NUM_REST-1:
                    print(' ', end='')
                else:
                    print('|')
    else:
        print('')

出力例

四人麻雀22人・5卓・2人休み:組み合わせ
11回戦、同じ人との対戦は11回以内
players: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21]
11回線中、休む人のパターン [[0, 1], [2, 3], [4, 5], [6, 7], [8, 9], [10, 11], [12, 13], [14, 15], [16, 17], [18, 19], [20, 21]]
Done! loop:0, match max: 1, Retry: 0, Remaining 0:201
Done! loop:1, match max: 1, Retry: 118, Remaining 0:171 
Done! loop:2, match max: 1, Retry: 55843, Remaining 0:141 
Done! loop:3, match max: 2, Retry: 0, Remaining 0:125 1:92 
Done! loop:4, match max: 2, Retry: 29, Remaining 0:110 1:92 
Done! loop:5, match max: 2, Retry: 69, Remaining 0:96 1:90
Done! loop:6, match max: 2, Retry: 960, Remaining 0:82 1:88 
Done! loop:7, match max: 2, Retry: 28700, Remaining 0:66 1:90 
Done! loop:8, match max: 2, Retry: 32087, Remaining 0:51 1:90 
Done! loop:9, match max: 3, Retry: 0, Remaining 0:46 1:84 2:87 
Done! loop:10, match max: 3, Retry: 11, Remaining 0:40 1:78 2:87 
マッチ回数表:3回以下
  2 2 2 1 0 2 0 0 1 3 2 3 3 1 1 1 2 1 2 0 1
    1 0 3 2 1 1 1 3 1 2 1 1 3 0 1 0 2 2 1 2
      2 2 3 1 0 3 0 2 1 1 1 1 3 1 2 2 0 0 2
        1 2 2 1 0 1 2 2 1 2 2 2 0 2 2 1 1 2
          2 1 2 1 1 2 1 2 1 2 1 2 0 2 1 1 1 
            1 2 3 1 1 2 0 1 2 1 2 2 2 1 0 0
              2 0 1 2 3 1 2 1 3 2 0 1 1 1 2
                2 1 2 2 1 2 3 1 2 2 0 2 2 0
                  1 2 1 1 2 0 2 0 3 3 2 1 2
                    1 3 2 0 3 1 2 1 2 1 3 1
                      0 0 3 1 0 2 2 2 2 0 0
                        2 0 1 1 0 1 1 1 3 1
                          2 0 0 1 3 2 2 3 2
                            0 1 2 1 0 3 1 2
                              3 2 0 0 1 2 2
                                2 0 2 2 2 2
                                  3 0 0 2 3
                                    1 2 2 1
                                      1 2 2
                                        2 1
                                          1

対戦組み合わせ
01回|21 15 06 11|18 09 08 10|19 20 07 14|17 13 12 03|04 02 05 16|休|00 01|
02回|16 15 14 09|10 00 06 13|18 05 19 11|17 08 21 20|07 04 12 01|休|02 03|
03回|16 17 07 10|03 14 02 21|12 18 06 20|01 09 00 11|15 08 19 13|休|04 05|
04回|05 18 17 03|21 16 15 14|10 02 04 08|12 20 11 09|13 01 00 19|休|06 07|
05回|02 03 05 15|00 18 12 04|01 11 14 20|07 10 17 19|21 13 06 16|休|08 09|
06回|16 13 20 07|15 06 02 00|12 17 19 08|04 05 01 14|09 21 03 18|休|10 11|
07回|15 19 03 20|10 05 16 06|21 08 18 01|11 00 02 17|14 07 04 09|休|12 13|
08回|18 01 02 10|05 13 08 07|20 16 09 17|12 19 00 21|11 06 04 03|休|14 15|
09回|00 03 10 14|12 02 21 13|06 01 19 09|05 08 07 11|04 15 18 20|休|16 17|
10回|11 09 20 12|17 05 02 08|14 06 15 07|04 21 01 16|00 13 10 03|休|18 19|
11回|03 07 11 06|13 19 10 04|08 18 02 15|16 17 00 12|01 05 09 14|休|20 21|
四人麻雀13人・3卓・1人休み:組み合わせ
13回戦、同じ人との対戦は13回以内
players: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]
13回線中、休む人のパターン [[0], [1], [2], [3], [4], [5], [6], [7], [8], [9], [10], [11], [12]]
Done! loop:0, match max: 1, Retry: 0, Remaining 0:60
Done! loop:1, match max: 2, Retry: 0, Remaining 0:44 1:32 
Done! loop:2, match max: 2, Retry: 0, Remaining 0:32 1:38 
Done! loop:3, match max: 2, Retry: 12, Remaining 0:20 1:44
Done! loop:4, match max: 2, Retry: 44, Remaining 0:15 1:36
Done! loop:5, match max: 2, Retry: 2980, Remaining 0:9 1:30 
Done! loop:6, match max: 3, Retry: 0, Remaining 0:8 1:25 2:34 
Done! loop:7, match max: 3, Retry: 8, Remaining 0:6 1:22 2:28 
Done! loop:8, match max: 4, Retry: 0, Remaining 0:3 1:20 2:27 3:24 
Done! loop:9, match max: 4, Retry: 1, Remaining 0:1 1:18 2:25 3:24 
Done! loop:10, match max: 4, Retry: 3, Remaining 0:1 1:12 2:26 3:22
Done! loop:11, match max: 4, Retry: 107, Remaining 0:0 1:8 2:22 3:28
Done! loop:12, match max: 4, Retry: 521, Remaining 0:0 1:7 2:13 3:31 
マッチ回数表:4回以下
  3 3 4 3 1 3 4 2 2 4 4 3
    4 1 3 4 3 1 2 4 3 4 4
      4 3 4 2 2 3 3 1 4 3
        3 4 4 3 3 3 3 3 1
          4 3 3 3 3 3 2 3
            2 3 2 4 4 2 2
              4 4 3 3 1 4
                2 4 4 4 2
                  4 3 4 4
                    2 1 3
                      3 3
                        4

対戦組み合わせ
01回|03 04 09 05|12 07 06 10|11 01 02 08|休|00|
02回|06 05 02 03|00 08 09 10|07 12 11 04|休|01|
03回|04 09 06 08|07 03 10 00|12 11 01 05|休|02|
04回|05 07 08 11|02 09 12 00|10 06 04 01|休|03|
05回|02 10 03 11|07 00 08 06|05 12 01 09|休|04|
06回|11 06 00 03|04 08 12 10|02 07 01 09|休|05|
07回|02 01 09 05|08 10 12 11|03 07 04 00|休|06|
08回|05 03 04 02|11 00 10 01|09 06 08 12|休|07|
09回|02 12 01 06|03 05 10 00|07 09 11 04|休|08|
10回|01 05 10 04|08 03 12 06|07 11 02 00|休|09|
11回|06 07 09 03|11 01 12 00|04 05 02 08|休|10|
12回|10 07 06 05|02 00 04 12|01 08 03 09|休|11|
13回|11 03 08 02|00 01 04 06|07 05 09 10|休|12|

コメント

タイトルとURLをコピーしました