예제 #1
0
    def testBuildChoices(self):
        import logging
        from thb.common import build_choices
        import random
        from thb.characters.baseclasses import get_characters
        from thb import characters
        from game import autoenv
        # def build_choices(g, items, candidates, players, num, akaris, shared):

        log = logging.getLogger('test')

        g = ObjectDict({
            'players': BatchList([ObjectDict({
                'account': ObjectDict({'userid': i}),
                'reveal': lambda o, i=i: log.info('Reveal to %s: %s', i, o),
            }) for i in xrange(8)]),
            'random': random.Random(12341234),
            'SERVER_SIDE': True,
            'CLIENT_SIDE': False,
        })
        autoenv.Game.getgame = staticmethod(lambda: g)
        chars = get_characters('common', '3v3')
        assert chars

        choices, imperial = build_choices(g, {}, chars, g.players, 10, 3, True)
        eq_(len(choices.items()), len(g.players))
        eq_(len(set([id(i) for i in choices.values()])), 1)
        eq_(set(choices.keys()), set(g.players))
        eq_(imperial, [])

        choices, imperial = build_choices(g, {0: ['imperial-choice:SpAya', 'foo']}, chars, g.players, 10, 3, True)
        eq_(len(choices.items()), len(g.players))
        eq_(len(set([id(i) for i in choices.values()])), 1)
        eq_(set(choices.keys()), set(g.players))
        p, c = imperial[0]
        eq_((p, c.char_cls), (g.players[0], characters.sp_aya.SpAya))
        assert c in choices[p]
        del c
        eq_(sum([c.akari for c in choices[p]]), 3)

        choices, imperial = build_choices(g, {0: ['imperial-choice:SpAya', 'foo']}, chars, g.players, [4] * 8, [1] * 8, False)
        eq_(len(choices.items()), len(g.players))
        eq_(len(set([id(i) for i in choices.values()])), 8)
        eq_(set(choices.keys()), set(g.players))
        eq_([len(i) for i in choices.values()], [4] * 8)
        eq_([len([j for j in i if j.akari]) for i in choices.values()], [1] * 8)

        p, c = imperial[0]
        eq_((p, c.char_cls), (g.players[0], characters.sp_aya.SpAya))
        assert c in choices[p]
예제 #2
0
    def testBuildChoices(self):
        import logging
        from thb.common import build_choices
        import random
        from thb.characters.baseclasses import get_characters
        from thb import characters
        from game import autoenv
        # def build_choices(g, items, candidates, players, num, akaris, shared):

        log = logging.getLogger('test')

        g = ObjectDict({
            'players':
            BatchList([
                ObjectDict({
                    'account':
                    ObjectDict({'userid': i}),
                    'reveal':
                    lambda o, i=i: log.info('Reveal to %s: %s', i, o),
                }) for i in xrange(8)
            ]),
            'random':
            random.Random(12341234),
            'SERVER_SIDE':
            True,
            'CLIENT_SIDE':
            False,
        })
        autoenv.Game.getgame = staticmethod(lambda: g)
        chars = get_characters('common', '3v3')
        assert chars

        choices, imperial = build_choices(g, {}, chars, g.players, 10, 3, True)
        eq_(len(choices.items()), len(g.players))
        eq_(len(set([id(i) for i in choices.values()])), 1)
        eq_(set(choices.keys()), set(g.players))
        eq_(imperial, [])

        choices, imperial = build_choices(
            g, {0: ['imperial-choice:SpAya', 'foo']}, chars, g.players, 10, 3,
            True)
        eq_(len(choices.items()), len(g.players))
        eq_(len(set([id(i) for i in choices.values()])), 1)
        eq_(set(choices.keys()), set(g.players))
        p, c = imperial[0]
        eq_((p, c.char_cls), (g.players[0], characters.sp_aya.SpAya))
        assert c in choices[p]
        del c
        eq_(sum([c.akari for c in choices[p]]), 3)

        choices, imperial = build_choices(
            g, {0: ['imperial-choice:SpAya', 'foo']}, chars, g.players,
            [4] * 8, [1] * 8, False)
        eq_(len(choices.items()), len(g.players))
        eq_(len(set([id(i) for i in choices.values()])), 8)
        eq_(set(choices.keys()), set(g.players))
        eq_([len(i) for i in choices.values()], [4] * 8)
        eq_([len([j for j in i if j.akari]) for i in choices.values()],
            [1] * 8)

        p, c = imperial[0]
        eq_((p, c.char_cls), (g.players[0], characters.sp_aya.SpAya))
        assert c in choices[p]
예제 #3
0
파일: thb3v3.py 프로젝트: feisuzhu/thbattle
    def apply_action(self):
        g = Game.getgame()
        params = self.params

        from thb.cards import Deck

        g.deck = Deck()
        g.ehclasses = []

        if params['random_seat']:
            seed = get_seed_for(g.players)
            random.Random(seed).shuffle(g.players)
            g.emit_event('reseat', None)

        for i, p in enumerate(g.players):
            p.identity = Identity()
            p.identity.type = (Identity.TYPE.HAKUREI, Identity.TYPE.MORIYA)[i % 2]

        g.forces = forces = BatchList([BatchList(), BatchList()])
        for i, p in enumerate(g.players):
            f = i % 2
            p.force = f
            forces[f].append(p)

        pl = g.players
        for p in pl:
            g.process_action(RevealIdentity(p, pl))

        from . import characters
        chars = characters.get_characters('common', '3v3')
        choices, imperial_choices = build_choices(
            g, self.items,
            candidates=chars, players=g.players,
            num=16, akaris=4, shared=True,
        )

        roll_rst = roll(g, self.items)
        first = roll_rst[0]
        first_index = g.players.index(first)

        order_list   = (0, 5, 3, 4, 2, 1)
        n = len(order_list)
        order = [g.players[(first_index + i) % n] for i in order_list]

        akaris = []
        with InputTransaction('ChooseGirl', g.players, mapping=choices) as trans:
            chosen = set()

            for p, c in imperial_choices:
                chosen.add(p)
                c.chosen = p
                g.set_character(p, c.char_cls)
                trans.notify('girl_chosen', (p, c))

            for p in order:
                if p in chosen:
                    continue

                c = user_input([p], ChooseGirlInputlet(g, choices), timeout=30, trans=trans)
                c = c or [_c for _c in reversed(choices[p]) if not _c.chosen][0]
                c.chosen = p

                if c.akari:
                    c.akari = False
                    akaris.append((p, c))
                else:
                    g.set_character(p, c.char_cls)

                trans.notify('girl_chosen', (p, c))

        # reveal akaris
        if akaris:
            g.players.reveal([i[1] for i in akaris])

            for p, c in akaris:
                g.set_character(p, c.char_cls)

        # -------
        for p in g.players:
            log.info(
                u'>> Player: %s:%s %s',
                p.__class__.__name__,
                Identity.TYPE.rlookup(p.identity.type),
                p.account.username,
            )
        # -------

        first = g.players[first_index]

        g.emit_event('game_begin', g)

        for p in g.players:
            g.process_action(DrawCards(p, amount=3 if p is first else 4))

        pl = g.players.rotate_to(first)

        for i, p in enumerate(cycle(pl)):
            if i >= 6000: break
            if not p.dead:
                g.emit_event('player_turn', p)
                try:
                    g.process_action(PlayerTurn(p))
                except InterruptActionFlow:
                    pass

        return True
예제 #4
0
    def apply_action(self):
        g = Game.getgame()
        params = self.params

        from thb.cards import Deck

        g.picks = []
        g.deck = Deck()

        g.ehclasses = list(action_eventhandlers) + g.game_ehs.values()

        H, M = Identity.TYPE.HAKUREI, Identity.TYPE.MORIYA
        if params['random_seat']:
            # reseat
            seed = get_seed_for(g.players)
            random.Random(seed).shuffle(g.players)
            g.emit_event('reseat', None)

            L = [[H, H, M, M, H, M], [H, M, H, M, H, M]]
            rnd = random.Random(get_seed_for(g.players))
            L = rnd.choice(L) * 2
            s = rnd.randrange(0, 6)
            idlist = L[s:s+6]
            del L, s, rnd
        else:
            idlist = [H, M, H, M, H, M]

        del H, M

        for p, identity in zip(g.players, idlist):
            p.identity = Identity()
            p.identity.type = identity
            g.process_action(RevealIdentity(p, g.players))

        force_hakurei      = BatchList()
        force_moriya       = BatchList()
        force_hakurei.pool = []
        force_moriya.pool  = []

        for p in g.players:
            if p.identity.type == Identity.TYPE.HAKUREI:
                force_hakurei.append(p)
                p.force = force_hakurei
            elif p.identity.type == Identity.TYPE.MORIYA:
                force_moriya.append(p)
                p.force = force_moriya

        g.forces = BatchList([force_hakurei, force_moriya])

        roll_rst = roll(g, self.items)
        first = roll_rst[0]

        # choose girls -->
        from . import characters
        chars = characters.get_characters('common', 'faith')

        choices, _ = build_choices(
            g, self.items,
            candidates=chars, players=g.players,
            num=[4] * 6, akaris=[1] * 6,
            shared=False,
        )

        rst = user_input(g.players, SortCharacterInputlet(g, choices, 2), timeout=30, type='all')

        for p in g.players:
            a, b = [choices[p][i] for i in rst[p][:2]]
            b.chosen = None
            p.force.reveal(b)
            g.switch_character(p, a)
            p.force.pool.append(b)

        for p in g.players:
            if p.player is first:
                first = p
                break

        pl = g.players
        first_index = pl.index(first)
        order = BatchList(range(len(pl))).rotate_to(first_index)

        for p in pl:
            g.process_action(RevealIdentity(p, pl))

        g.emit_event('game_begin', g)

        for p in pl:
            g.process_action(DistributeCards(p, amount=4))

        pl = g.players.rotate_to(first)
        rst = user_input(pl[1:], ChooseOptionInputlet(DeathHandler(), (False, True)), type='all')

        for p in pl[1:]:
            rst.get(p) and g.process_action(RedrawCards(p, p))

        pl = g.players
        for i, idx in enumerate(cycle(order)):
            if i >= 6000: break
            p = pl[idx]
            if p.dead: continue

            g.emit_event('player_turn', p)
            try:
                g.process_action(PlayerTurn(p))
            except InterruptActionFlow:
                pass

        return True
예제 #5
0
파일: thbkof.py 프로젝트: zzkklep/thbattle
    def apply_action(self):
        g = Game.getgame()

        from . import cards

        g.pick_history = []

        g.deck = cards.Deck(cards.kof_card_definition)
        g.ehclasses = []
        g.current_player = None

        for i, p in enumerate(g.players):
            p.identity = Identity()
            p.identity.type = (Identity.TYPE.HAKUREI, Identity.TYPE.MORIYA)[i % 2]

        # choose girls -->
        from thb.characters import get_characters
        chars = get_characters('common', 'kof')

        A, B = roll(g, self.items)
        order = [A, B, B, A, A, B, B, A, A, B]

        choices, imperial_choices = build_choices(
            g, self.items,
            candidates=chars, players=[A, B],
            num=10, akaris=4, shared=True,
        )

        chosen = {A: [], B: []}

        with InputTransaction('ChooseGirl', g.players, mapping=choices) as trans:
            for p, c in imperial_choices:
                c.chosen = p
                chosen[p].append(c)
                trans.notify('girl_chosen', (p, c))
                order.remove(p)

            for p in order:
                c = user_input([p], ChooseGirlInputlet(g, choices), 10, 'single', trans)
                c = c or first(choices[p], lambda c: not c.chosen)

                c.chosen = p
                chosen[p].append(c)

                trans.notify('girl_chosen', (p, c))

        # reveal akaris for themselves
        for p in [A, B]:
            for c in chosen[p]:
                c.akari = False
                p.reveal(c)
                del c.chosen

        list_shuffle(chosen[A], A)
        list_shuffle(chosen[B], B)

        with InputTransaction('ChooseGirl', g.players, mapping=chosen) as trans:
            ilet = ChooseGirlInputlet(g, chosen)
            ilet.with_post_process(lambda p, rst: trans.notify('girl_chosen', (p, rst)) or rst)
            rst = user_input([A, B], ilet, type='all', trans=trans)

        def s(p):
            c = rst[p] or chosen[p][0]
            chosen[p].remove(c)
            p.choices = chosen[p]
            p.remaining = [2]
            p = g.next_character(p, c)
            return p

        A, B = s(A), s(B)

        order = [1, 0] if A is g.players[0] else [0, 1]

        for p in [A, B]:
            g.process_action(RevealIdentity(p, g.players))

        g.emit_event('game_begin', g)

        g.process_action(DistributeCards(A, amount=4))
        g.process_action(DistributeCards(B, amount=3))

        for i in order:
            g.emit_event('character_debut', (None, g.players[i]))

        for i, idx in enumerate(cycle(order)):
            p = g.players[idx]
            if i >= 6000: break
            if p.dead:
                KOFCharacterSwitchHandler.do_switch_dead()
                p = g.players[idx]  # player changed

            assert not p.dead

            try:
                g.emit_event('player_turn', p)
                g.process_action(PlayerTurn(p))
            except InterruptActionFlow:
                pass
예제 #6
0
    def apply_action(self):
        g = Game.getgame()
        params = self.params

        from thb.cards import Deck

        g.deck = Deck()
        g.ehclasses = []

        # arrange identities -->
        g.double_curtain = params['double_curtain']

        mapping = {
            'B': Identity.TYPE.BOSS,
            '!': Identity.TYPE.ATTACKER,
            '&': Identity.TYPE.ACCOMPLICE,
            '?': Identity.TYPE.CURTAIN,
        }

        if g.double_curtain:
            identities = 'B!!!&&??'
        else:
            identities = 'B!!!!&&?'

        pl = g.players[:]
        identities = [mapping[i] for i in identities]
        g.identities = identities[:]
        imperial_identities = ImperialIdentity.get_chosen(self.items, pl)
        for p, i in imperial_identities:
            pl.remove(p)
            identities.remove(i)

        g.random.shuffle(identities)

        if Game.CLIENT_SIDE:
            identities = [Identity.TYPE.HIDDEN for _ in identities]

        for p, i in imperial_identities + zip(pl, identities):
            p.identity = Identity()
            p.identity.type = i
            g.process_action(RevealIdentity(p, p))

        del identities

        is_boss = sync_primitive([p.identity.type == Identity.TYPE.BOSS for p in g.players], g.players)
        boss_idx = is_boss.index(True)
        boss = g.boss = g.players[boss_idx]

        boss.identity = Identity()
        boss.identity.type = Identity.TYPE.BOSS
        g.process_action(RevealIdentity(boss, g.players))

        # choose girls init -->
        from .characters import get_characters
        pl = g.players.rotate_to(boss)

        choices, _ = build_choices(
            g, self.items,
            candidates=get_characters('common', 'id', 'id8', '-boss'),
            players=[boss],
            num=[5], akaris=[1],
            shared=False,
        )

        choices[boss][:0] = [CharChoice(cls) for cls in get_characters('boss')]

        with InputTransaction('ChooseGirl', [boss], mapping=choices) as trans:
            c = user_input([boss], ChooseGirlInputlet(g, choices), 30, 'single', trans)

            c = c or choices[boss][-1]
            c.chosen = boss
            c.akari = False
            g.players.reveal(c)
            trans.notify('girl_chosen', (boss, c))

        chars = get_characters('common', 'id', 'id8')

        try:
            chars.remove(c.char_cls)
        except:
            pass

        # mix it in advance
        # so the others could see it

        boss = g.switch_character(boss, c.char_cls)

        # boss's hp bonus
        if g.n_persons > 5:
            boss.maxlife += 1

        boss.life = boss.maxlife

        # choose boss dedicated skill
        g.process_action(ChooseBossSkillAction(boss, boss))

        # reseat
        seed = get_seed_for(g.players)
        random.Random(seed).shuffle(g.players)
        g.emit_event('reseat', None)

        # others choose girls
        pl = g.players.exclude(boss)

        choices, _ = build_choices(
            g, self.items,
            candidates=chars, players=pl,
            num=[4] * len(pl), akaris=[1] * len(pl),
            shared=False,
        )

        with InputTransaction('ChooseGirl', pl, mapping=choices) as trans:
            ilet = ChooseGirlInputlet(g, choices)
            ilet.with_post_process(lambda p, rst: trans.notify('girl_chosen', (p, rst)) or rst)
            result = user_input(pl, ilet, type='all', trans=trans)

        # mix char class with player -->
        for p in pl:
            c = result[p] or choices[p][-1]
            c.akari = False
            g.players.reveal(c)
            p = g.switch_character(p, c.char_cls)

        # -------
        for p in g.players:
            log.info(
                u'>> Player: %s:%s %s',
                p.__class__.__name__,
                Identity.TYPE.rlookup(p.identity.type),
                p.account.username,
            )
        # -------

        g.emit_event('game_begin', g)

        for p in g.players:
            g.process_action(DistributeCards(p, amount=4))

        for i, p in enumerate(cycle(g.players.rotate_to(boss))):
            if i >= 6000: break
            if not p.dead:
                try:
                    g.process_action(PlayerTurn(p))
                except InterruptActionFlow:
                    pass

        return True
예제 #7
0
    def apply_action(self):
        g = Game.getgame()
        params = self.params

        from thb.cards import Deck

        g.picks = []
        g.deck = Deck()

        g.ehclasses = list(action_eventhandlers) + g.game_ehs.values()

        H, M = Identity.TYPE.HAKUREI, Identity.TYPE.MORIYA
        if params['random_seat']:
            # reseat
            seed = get_seed_for(g.players)
            random.Random(seed).shuffle(g.players)
            g.emit_event('reseat', None)

            L = [[H, H, M, M, H, M], [H, M, H, M, H, M]]
            rnd = random.Random(get_seed_for(g.players))
            L = rnd.choice(L) * 2
            s = rnd.randrange(0, 6)
            idlist = L[s:s + 6]
            del L, s, rnd
        else:
            idlist = [H, M, H, M, H, M]

        del H, M

        for p, identity in zip(g.players, idlist):
            p.identity = Identity()
            p.identity.type = identity
            g.process_action(RevealIdentity(p, g.players))

        force_hakurei = BatchList()
        force_moriya = BatchList()
        force_hakurei.pool = []
        force_moriya.pool = []

        for p in g.players:
            if p.identity.type == Identity.TYPE.HAKUREI:
                force_hakurei.append(p)
                p.force = force_hakurei
            elif p.identity.type == Identity.TYPE.MORIYA:
                force_moriya.append(p)
                p.force = force_moriya

        g.forces = BatchList([force_hakurei, force_moriya])

        roll_rst = roll(g, self.items)
        first = roll_rst[0]

        # choose girls -->
        from . import characters
        chars = characters.get_characters('common', 'faith')

        choices, _ = build_choices(
            g,
            self.items,
            candidates=chars,
            players=g.players,
            num=[4] * 6,
            akaris=[1] * 6,
            shared=False,
        )

        rst = user_input(g.players,
                         SortCharacterInputlet(g, choices, 2),
                         timeout=30,
                         type='all')

        for p in g.players:
            a, b = [choices[p][i] for i in rst[p][:2]]
            b.chosen = None
            p.force.reveal(b)
            g.switch_character(p, a)
            p.force.pool.append(b)

        for p in g.players:
            if p.player is first:
                first = p
                break

        pl = g.players
        first_index = pl.index(first)
        order = BatchList(range(len(pl))).rotate_to(first_index)

        for p in pl:
            g.process_action(RevealIdentity(p, pl))

        g.emit_event('game_begin', g)

        for p in pl:
            g.process_action(DistributeCards(p, amount=4))

        pl = g.players.rotate_to(first)
        rst = user_input(pl[1:],
                         ChooseOptionInputlet(DeathHandler(), (False, True)),
                         type='all')

        for p in pl[1:]:
            rst.get(p) and g.process_action(RedrawCards(p, p))

        pl = g.players
        for i, idx in enumerate(cycle(order)):
            if i >= 6000: break
            p = pl[idx]
            if p.dead: continue

            g.emit_event('player_turn', p)
            try:
                g.process_action(PlayerTurn(p))
            except InterruptActionFlow:
                pass

        return True
예제 #8
0
파일: thb3v3.py 프로젝트: zzkklep/thbattle
    def apply_action(self):
        g = Game.getgame()
        params = self.params

        from thb.cards import Deck

        g.deck = Deck()
        g.ehclasses = []

        if params['random_seat']:
            seed = get_seed_for(g.players)
            random.Random(seed).shuffle(g.players)
            g.emit_event('reseat', None)

        for i, p in enumerate(g.players):
            p.identity = Identity()
            p.identity.type = (Identity.TYPE.HAKUREI,
                               Identity.TYPE.MORIYA)[i % 2]

        g.forces = forces = BatchList([BatchList(), BatchList()])
        for i, p in enumerate(g.players):
            f = i % 2
            p.force = f
            forces[f].append(p)

        pl = g.players
        for p in pl:
            g.process_action(RevealIdentity(p, pl))

        from . import characters
        chars = characters.get_characters('common', '3v3')
        choices, imperial_choices = build_choices(
            g,
            self.items,
            candidates=chars,
            players=g.players,
            num=16,
            akaris=4,
            shared=True,
        )

        roll_rst = roll(g, self.items)
        first = roll_rst[0]
        first_index = g.players.index(first)

        order_list = (0, 5, 3, 4, 2, 1)
        n = len(order_list)
        order = [g.players[(first_index + i) % n] for i in order_list]

        akaris = []
        with InputTransaction('ChooseGirl', g.players,
                              mapping=choices) as trans:
            chosen = set()

            for p, c in imperial_choices:
                chosen.add(p)
                c.chosen = p
                g.set_character(p, c.char_cls)
                trans.notify('girl_chosen', (p, c))

            for p in order:
                if p in chosen:
                    continue

                c = user_input([p],
                               ChooseGirlInputlet(g, choices),
                               timeout=30,
                               trans=trans)
                c = c or [_c
                          for _c in reversed(choices[p]) if not _c.chosen][0]
                c.chosen = p

                if c.akari:
                    c.akari = False
                    akaris.append((p, c))
                else:
                    g.set_character(p, c.char_cls)

                trans.notify('girl_chosen', (p, c))

        # reveal akaris
        if akaris:
            g.players.reveal([i[1] for i in akaris])

            for p, c in akaris:
                g.set_character(p, c.char_cls)

        # -------
        for p in g.players:
            log.info(
                u'>> Player: %s:%s %s',
                p.__class__.__name__,
                Identity.TYPE.rlookup(p.identity.type),
                p.account.username,
            )
        # -------

        first = g.players[first_index]

        g.emit_event('game_begin', g)

        for p in g.players:
            g.process_action(DrawCards(p, amount=3 if p is first else 4))

        pl = g.players.rotate_to(first)

        for i, p in enumerate(cycle(pl)):
            if i >= 6000: break
            if not p.dead:
                g.emit_event('player_turn', p)
                try:
                    g.process_action(PlayerTurn(p))
                except InterruptActionFlow:
                    pass

        return True
예제 #9
0
    def apply_action(self):
        g = Game.getgame()
        params = self.params

        from thb.cards import Deck

        g.deck = Deck()
        g.ehclasses = []

        # arrange identities -->
        g.double_curtain = params['double_curtain']

        mapping = {
            'B': Identity.TYPE.BOSS,
            '!': Identity.TYPE.ATTACKER,
            '&': Identity.TYPE.ACCOMPLICE,
            '?': Identity.TYPE.CURTAIN,
        }

        if g.double_curtain:
            identities = 'B!!!&&??'
        else:
            identities = 'B!!!!&&?'

        pl = g.players[:]
        identities = [mapping[i] for i in identities]
        g.identities = identities[:]
        imperial_identities = ImperialIdentity.get_chosen(self.items, pl)
        for p, i in imperial_identities:
            pl.remove(p)
            identities.remove(i)

        g.random.shuffle(identities)

        if Game.CLIENT_SIDE:
            identities = [Identity.TYPE.HIDDEN for _ in identities]

        for p, i in imperial_identities + zip(pl, identities):
            p.identity = Identity()
            p.identity.type = i
            g.process_action(RevealIdentity(p, p))

        del identities

        is_boss = sync_primitive(
            [p.identity.type == Identity.TYPE.BOSS for p in g.players],
            g.players)
        boss_idx = is_boss.index(True)
        boss = g.boss = g.players[boss_idx]

        boss.identity = Identity()
        boss.identity.type = Identity.TYPE.BOSS
        g.process_action(RevealIdentity(boss, g.players))

        # choose girls init -->
        from .characters import get_characters
        pl = g.players.rotate_to(boss)

        choices, _ = build_choices(
            g,
            self.items,
            candidates=get_characters('common', 'id', 'id8', '-boss'),
            players=[boss],
            num=[5],
            akaris=[1],
            shared=False,
        )

        choices[boss][:0] = [CharChoice(cls) for cls in get_characters('boss')]

        with InputTransaction('ChooseGirl', [boss], mapping=choices) as trans:
            c = user_input([boss], ChooseGirlInputlet(g, choices), 30,
                           'single', trans)

            c = c or choices[boss][-1]
            c.chosen = boss
            c.akari = False
            g.players.reveal(c)
            trans.notify('girl_chosen', (boss, c))

        chars = get_characters('common', 'id', 'id8')

        try:
            chars.remove(c.char_cls)
        except:
            pass

        # mix it in advance
        # so the others could see it

        boss = g.switch_character(boss, c.char_cls)

        # boss's hp bonus
        if g.n_persons > 5:
            boss.maxlife += 1

        boss.life = boss.maxlife

        # choose boss dedicated skill
        g.process_action(ChooseBossSkillAction(boss, boss))

        # reseat
        seed = get_seed_for(g.players)
        random.Random(seed).shuffle(g.players)
        g.emit_event('reseat', None)

        # others choose girls
        pl = g.players.exclude(boss)

        choices, _ = build_choices(
            g,
            self.items,
            candidates=chars,
            players=pl,
            num=[4] * len(pl),
            akaris=[1] * len(pl),
            shared=False,
        )

        with InputTransaction('ChooseGirl', pl, mapping=choices) as trans:
            ilet = ChooseGirlInputlet(g, choices)
            ilet.with_post_process(
                lambda p, rst: trans.notify('girl_chosen', (p, rst)) or rst)
            result = user_input(pl, ilet, type='all', trans=trans)

        # mix char class with player -->
        for p in pl:
            c = result[p] or choices[p][-1]
            c.akari = False
            g.players.reveal(c)
            p = g.switch_character(p, c.char_cls)

        # -------
        for p in g.players:
            log.info(
                u'>> Player: %s:%s %s',
                p.__class__.__name__,
                Identity.TYPE.rlookup(p.identity.type),
                p.account.username,
            )
        # -------

        g.emit_event('game_begin', g)

        for p in g.players:
            g.process_action(DistributeCards(p, amount=4))

        for i, p in enumerate(cycle(g.players.rotate_to(boss))):
            if i >= 6000: break
            if not p.dead:
                try:
                    g.process_action(PlayerTurn(p))
                except InterruptActionFlow:
                    pass

        return True