Skip to content

Instantly share code, notes, and snippets.

@sasairc
Last active March 10, 2019 11:07
Show Gist options
  • Save sasairc/2b62c09e84f6240563ddbec0db5e10d9 to your computer and use it in GitHub Desktop.
Save sasairc/2b62c09e84f6240563ddbec0db5e10d9 to your computer and use it in GitHub Desktop.
二宮飛鳥は・・・?

二宮飛鳥は・・・?

面白そうだったので、ガシャを再現する簡易的なライブラリを作成しました。

gasha.h

/*
 * gasha.h - 二宮飛鳥?
 */

#ifndef GASHA_H
#define GASHA_H
#ifdef  __cplusplus
extern "C" {
/* __cplusplus */
#endif

#include <stdint.h> /* uint32_t */
#include <stdio.h>  /* size_t */

/*
 * レアリティの定義
 */
#define RARITY_R    3
#define RARITY_SR   4
#define RARITY_SSR  5

/*
 * レアリティに対する確率の定義
 */
#define DEFAULT_WEIGHT_R    85
#define DEFAULT_WEIGHT_SR   12
#define DEFAULT_WEIGHT_SSR  3

/*
 * カード(各キャラクター)
 */
typedef struct GASHA_CARD {
    uint32_t    id;
    char*       name;
    uint32_t    rarity;
} GASHA_CARD;

/*
 * レアリティ・確率の設定
 */
typedef struct GASHA_CONF {
    uint32_t    weights[6];
} GASHA_CONF;

/*
 * このライブラリの要
 */
typedef struct GASHA GASHA;
typedef struct GASHA {
    GASHA_CONF*     conf;
    GASHA_CARD**    card;
    size_t          cardc;
    int             (*change_weight)(GASHA** gasha, uint32_t rarity, uint32_t weight);
    int             (*join_cards)(GASHA** gasha, GASHA_CARD cards[]);
    int             (*is_ready)(GASHA* gasha);
    uint32_t        (*roll)(GASHA* gasha);
    uint32_t        (*roll10)(GASHA* gasha);
    void            (*release)(GASHA* gasha);
} GASHA;

int init_gasha(GASHA** gasha);
GASHA_CARD* id2card(GASHA* gasha, uint32_t id);

size_t count_by_rarity(GASHA* gasha, uint32_t rarity);
GASHA_CARD** filter_by_rarity(GASHA* gasha, uint32_t rarity);

#ifdef  __cplusplus
}
/* __cplusplus */
#endif
/* GASHA_H */
#endif

使用例

単発

単発で100回試行します。確率は以下の通り。

SSR  3%
SR  12%
R   85%
/*
 * main.c
 */
#include "./gasha.h"
#include <stdio.h>
#include <unistd.h>  /* usleep() */

int main(void)
{
    size_t      i           = 0;

    GASHA*      gasha       = NULL;

    GASHA_CARD* result      = NULL;

    GASHA_CARD  cards[]     = {
        {3001, "あ",            3}, /* R */
        {3002, "い",            3},
        {3003, "う",            3},
        {3004, "え",            3},
        {3005, "お",            3},
        {4001, "か",            4}, /* SR */
        {4002, "き",            4},
        {4003, "く",            4},
        {4004, "け",            4},
        {4005, "こ",            4},
        {5001, "うえきちゃん",  5}, /* SSR */
        {5002, "一ノ瀬志希",    5},
        {5003, "高垣楓",        5},
        {5004, "多田李衣菜",    5},
        {5005, "二宮飛鳥",      5},
        {5006, "ドラえもん",    5},
        {0, NULL, 0},
    };

    /* gasha の初期化 */
    init_gasha(&gasha);
    /* gasha に正規化されたカードを登録 */
    gasha->join_cards(&gasha, cards);

    /* 登録されたカードを確認してみる */
    fprintf(stderr, "**** cards ****\n");
    for (i = 0; i < gasha->cardc; i++) {
        fprintf(stderr, "id = %d, name = %s, rarity = %d\n",
                gasha->card[i]->id, gasha->card[i]->name, gasha->card[i]->rarity);
    }
    fprintf(stderr, "**** cards ****\n\n");

    /* gasha がレディか確認 */
    if (gasha->is_ready(gasha)) {
        /* 単発で 100 回試行する */
        for (i = 1; i <= 100; i++) {
            /* 当選した id からカードの情報を取得する */
            if (i % 10 == 0)
                result = id2card(gasha, gasha->roll10(gasha));
            else
                result = id2card(gasha, gasha->roll(gasha));
            fprintf(stdout, "%zu 回目の試行で 星%d の %s が当たりました。やったね!\n",
                    i, result->rarity, result->name);
            usleep(10000);
        }
    }
    /* 使った後はお片付け */
    gasha->release(gasha);

    return 0;
}

結果

% gcc main.c gasha.c -o gasha
% ./gasha | grep 星5
**** cards ****
id = 3001, name = あ, rarity = 3
id = 3002, name = い, rarity = 3
id = 3003, name = う, rarity = 3
id = 3004, name = え, rarity = 3
id = 3005, name = お, rarity = 3
id = 4001, name = か, rarity = 4
id = 4002, name = き, rarity = 4
id = 4003, name = く, rarity = 4
id = 4004, name = け, rarity = 4
id = 4004, name = こ, rarity = 4
id = 5001, name = うえきちゃん, rarity = 5
id = 5002, name = 一ノ瀬志希, rarity = 5
id = 5003, name = 高垣楓, rarity = 5
id = 5004, name = 多田李衣菜, rarity = 5
id = 5005, name = 二宮飛鳥, rarity = 5
id = 5006, name = ドラえもん, rarity = 5
**** cards ****

バグじゃないよ!

10連

10連により星4以上のカードを1枚以上確定させます。 といっても、適宜 roll10() を呼ぶ実装なので main.c を以下のように変更。

<             result = id2card(gasha, gasha->roll(gasha));
---
>             if (i % 10 == 0)
>                 result = id2card(gasha, gasha->roll10(gasha));
>             else
>                 result = id2card(gasha, gasha->roll(gasha));

結果

% gcc main.c gasha.c -o gasha10
% ./gasha10 | grep -v 星3

(略)

10 回目の試行で 星4 の け が当たりました。やったね!
15 回目の試行で 星4 の き が当たりました。やったね!
20 回目の試行で 星4 の け が当たりました。やったね!
30 回目の試行で 星4 の き が当たりました。やったね!
34 回目の試行で 星4 の か が当たりました。やったね!
39 回目の試行で 星4 の き が当たりました。やったね!
40 回目の試行で 星4 の き が当たりました。やったね!
45 回目の試行で 星4 の く が当たりました。やったね!
50 回目の試行で 星5 の 二宮飛鳥 が当たりました。やったね!
52 回目の試行で 星4 の く が当たりました。やったね!
60 回目の試行で 星4 の け が当たりました。やったね!
62 回目の試行で 星4 の く が当たりました。やったね!
70 回目の試行で 星4 の か が当たりました。やったね!
72 回目の試行で 星4 の か が当たりました。やったね!
76 回目の試行で 星4 の き が当たりました。やったね!
80 回目の試行で 星4 の け が当たりました。やったね!
90 回目の試行で 星4 の き が当たりました。やったね!
100 回目の試行で 星4 の け が当たりました。やったね!

50 回目の試行で 星5 の 二宮飛鳥 が当たりました。やったね!

/*
* gasha.c - 二宮飛鳥?
*/
#include "./gasha.h"
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <sys/time.h>
#ifdef LIBRARY_VERBOSE
#define print_error() fprintf(stderr, "%s: %d: %s\n",\
__FILE__, __LINE__, strerror(errno))
/* LIBRARY_VERBOSE */
#endif
static int create_config(GASHA** gasha);
static int change_weight(GASHA** gasha, uint32_t rarity, uint32_t weight);
static int join_cards(GASHA** gasha, GASHA_CARD cards[]);
static int is_ready(GASHA* gasha);
static uint32_t roll(GASHA* gasha);
static uint32_t roll10(GASHA* gasha);
static uint32_t select_card(GASHA* gasha, uint32_t rarity);
static void release_card(GASHA** gasha);
static void release(GASHA* gasha);
static uint32_t gen_rval(uint32_t min, uint32_t max);
int init_gasha(GASHA** gasha)
{
GASHA* gs = NULL;
if ((gs = (GASHA*)
malloc(sizeof(GASHA))) == NULL) {
#ifdef LIBRARY_VERBOSE
print_error();
/* LIBRARY_VERBOSE */
#endif
goto ERR;
} else {
gs->conf = NULL;
gs->card = NULL;
gs->cardc = 0;
gs->join_cards = join_cards;
gs->change_weight = change_weight;
gs->is_ready = is_ready;
gs->roll = roll;
gs->roll10 = roll10;
gs->release = release;
}
if (create_config(&gs) < 0)
goto ERR;
*gasha = gs;
return 0;
ERR:
release(gs);
return -1;
}
/*
* id2card()
*
* roll() から返却された ID からカードの情報を得る
*/
GASHA_CARD*
id2card(GASHA* gasha, uint32_t id)
{
size_t i = 0;
for (i = 0; i < gasha->cardc; i++) {
if (gasha->card[i]->id == id)
return gasha->card[i];
}
return NULL;
}
/*
* count_by_rarity()
* filter_by_rarity()
*
* レアリティ別のリストを返す
*/
size_t
count_by_rarity(GASHA* gasha, uint32_t rarity)
{
size_t i = 0,
n = 0;
for (i = 0; i < gasha->cardc; i++)
if (gasha->card[i]->rarity == rarity)
n++;
return n;
}
GASHA_CARD**
filter_by_rarity(GASHA* gasha, uint32_t rarity)
{
size_t i = 0,
n = 0;
GASHA_CARD** dest = NULL;
if ((dest = (GASHA_CARD**)
malloc(sizeof(GASHA_CARD*) * (count_by_rarity(gasha, rarity) + 1))) == NULL) {
#ifdef LIBRARY_VERBOSE
print_error();
/* LIBRARY_VERBOSE */
#endif
return NULL;
}
for (i = 0; i < gasha->cardc; i++) {
if (gasha->card[i]->rarity == rarity) {
dest[n] = gasha->card[i];
n++;
}
}
dest[n] = NULL;
return dest;
}
/*
* create_config()
*
* レアリティに対する既定の確率を設定する
*/
static
int create_config(GASHA** gasha)
{
if (((*gasha)->conf = (GASHA_CONF*)
malloc(sizeof(GASHA_CONF))) == NULL) {
#ifdef LIBRARY_VERBOSE
print_error();
/* LIBRARY_VERBOSE */
#endif
return -1;
} else {
(*gasha)->conf->weights[RARITY_R] = DEFAULT_WEIGHT_R;
(*gasha)->conf->weights[RARITY_SR] = DEFAULT_WEIGHT_SR;
(*gasha)->conf->weights[RARITY_SSR] = DEFAULT_WEIGHT_SSR;
}
return 0;
}
/*
* change_weight()
*
* 確率を手動で設定する
*/
static
int change_weight(GASHA** gasha, uint32_t rarity, uint32_t weight)
{
if ((*gasha)->conf == NULL) {
if (create_config(gasha) < 0)
return -1;
}
(*gasha)->conf->weights[rarity] = weight;
return 0;
}
/*
* join_cards()
*
* gasha にカードを登録する
*/
static
int join_cards(GASHA** gasha, GASHA_CARD cards[])
{
size_t i = 0;
for ((*gasha)->cardc = 0;
cards[(*gasha)->cardc].name != NULL; ((*gasha)->cardc)++);
if (((*gasha)->card = (GASHA_CARD**)
malloc(sizeof(GASHA_CARD) * (*gasha)->cardc)) == NULL) {
#ifdef LIBRARY_VERBOSE
print_error();
/* LIBRARY_VERBOSE */
#endif
goto ERR;
}
for (i = 0; i < (*gasha)->cardc; i++) {
if (((*gasha)->card[i] = (GASHA_CARD*)
malloc(sizeof(GASHA_CARD))) == NULL) {
#ifdef LIBRARY_VERBOSE
print_error();
/* LIBRARY_VERBOSE */
#endif
goto ERR;
} else {
(*gasha)->card[i]->id = cards[i].id;
(*gasha)->card[i]->rarity = cards[i].rarity;
if (((*gasha)->card[i]->name = strdup(cards[i].name)) == NULL) {
#ifdef LIBRARY_VERBOSE
print_error();
/* LIBRARY_VERBOSE */
#endif
goto ERR;
}
}
}
return 0;
ERR:
release_card(gasha);
return -1;
}
/*
* select_card()
*
* 確定したレアリティのカードを一枚抽選する
*/
static
uint32_t select_card(GASHA* gasha, uint32_t rarity)
{
uint32_t id = 0;
GASHA_CARD** cards = NULL;
if ((cards = filter_by_rarity(gasha, rarity)) == NULL)
return 0;
id = cards[gen_rval(0, count_by_rarity(gasha, rarity))]->id;
free(cards);
return id;
}
/*
* is_ready()
*
* roll()/roll10() できる状態であるかチェックする
*/
static
int is_ready(GASHA* gasha)
{
if (gasha->conf != NULL &&
gasha->card != NULL &&
gasha->cardc > 0)
return 1;
return 0;
}
/*
* roll()
*
* 所謂「単発」
*/
static
uint32_t roll(GASHA* gasha)
{
uint32_t rval = gen_rval(1,
gasha->conf->weights[RARITY_R] +
gasha->conf->weights[RARITY_SR] +
gasha->conf->weights[RARITY_SSR]);
if (rval < gasha->conf->weights[RARITY_SSR])
return select_card(gasha, RARITY_SSR);
if (rval < gasha->conf->weights[RARITY_SR])
return select_card(gasha, RARITY_SR);
return select_card(gasha, RARITY_R);
}
/*
* roll10()
*
* 10連ガシャにおいて SR 以上を確定させる
* この関数が10連する訳ではなく、上記の roll() を組み合わせて使う
*/
static
uint32_t roll10(GASHA* gasha)
{
uint32_t rval = gen_rval(1, gasha->conf->weights[RARITY_SR]);
if (rval < gasha->conf->weights[RARITY_SSR])
return select_card(gasha, RARITY_SSR);
return select_card(gasha, RARITY_SR);
}
/*
* 以下はリソースの解放
* release() で全て呼ばれる
*/
static
void release_conf(GASHA** gasha)
{
if ((*gasha)->conf == NULL)
return;
free((*gasha)->conf);
(*gasha)->conf = NULL;
return;
}
static
void release_card(GASHA** gasha)
{
size_t i = 0;
if ((*gasha)->card == NULL)
return;
for (i = 0; i < (*gasha)->cardc; i++) {
if ((*gasha)->card[i] != NULL) {
if ((*gasha)->card[i]->name != NULL) {
free((*gasha)->card[i]->name);
(*gasha)->card[i]->name = NULL;
}
free((*gasha)->card[i]);
(*gasha)->card[i] = NULL;
}
}
free((*gasha)->card);
(*gasha)->card = NULL;
(*gasha)->cardc = 0;
return;
}
static
void release(GASHA* gasha)
{
if (gasha != NULL) {
release_conf(&gasha);
release_card(&gasha);
free(gasha);
}
return;
}
/*
* gen_rval()
*
* 乱数生成
*/
static
uint32_t gen_rval(uint32_t min, uint32_t max)
{
struct timeval tv;
gettimeofday(&tv, NULL);
srand(tv.tv_usec);
return (uint32_t)(rand() % max) + min;
}
/*
* gasha.h - 二宮飛鳥?
*/
#ifndef GASHA_H
#define GASHA_H
#ifdef __cplusplus
extern "C" {
/* __cplusplus */
#endif
#include <stdint.h> /* uint32_t */
#include <stdio.h> /* size_t */
/*
* レアリティの定義
*/
#define RARITY_R 3
#define RARITY_SR 4
#define RARITY_SSR 5
/*
* レアリティに対する確率の定義
*/
#define DEFAULT_WEIGHT_R 85
#define DEFAULT_WEIGHT_SR 12
#define DEFAULT_WEIGHT_SSR 3
/*
* カード(各キャラクター)
*/
typedef struct GASHA_CARD {
uint32_t id;
char* name;
uint32_t rarity;
} GASHA_CARD;
/*
* レアリティ・確率の設定
*/
typedef struct GASHA_CONF {
uint32_t weights[6];
} GASHA_CONF;
/*
* このライブラリの要
*/
typedef struct GASHA GASHA;
typedef struct GASHA {
GASHA_CONF* conf;
GASHA_CARD** card;
size_t cardc;
int (*change_weight)(GASHA** gasha, uint32_t rarity, uint32_t weight);
int (*join_cards)(GASHA** gasha, GASHA_CARD cards[]);
int (*is_ready)(GASHA* gasha);
uint32_t (*roll)(GASHA* gasha);
uint32_t (*roll10)(GASHA* gasha);
void (*release)(GASHA* gasha);
} GASHA;
int init_gasha(GASHA** gasha);
GASHA_CARD* id2card(GASHA* gasha, uint32_t id);
size_t count_by_rarity(GASHA* gasha, uint32_t rarity);
GASHA_CARD** filter_by_rarity(GASHA* gasha, uint32_t rarity);
#ifdef __cplusplus
}
/* __cplusplus */
#endif
/* GASHA_H */
#endif
@844196
Copy link

844196 commented Mar 10, 2019

1e0

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment