diff --git a/src/gamesbyexample/liars-pub.py b/src/gamesbyexample/liars-pub.py new file mode 100644 index 0000000..9646ae8 --- /dev/null +++ b/src/gamesbyexample/liars-pub.py @@ -0,0 +1,339 @@ +import random +import time + +# 全局变量 +target = None # 目标牌 +gun_chamber = [] # 左轮手枪弹巢 +gun_index = 0 # 当前子弹位置 +players = [] # 玩家列表 +active_players = [] # 活跃玩家列表(未被淘汰的玩家) +player_hands = {} # 玩家手牌 +current_player_index = 0 # 当前玩家索引 +deck = [] # 牌堆 +max_len = 0 + +import random, sys, time +# Python 2 compatibility for input (remove if only supporting Python 3) +if sys.version_info[0] < 3: + try: + input = raw_input + except NameError: + pass + +# cards +card_img = { + "J": [r"|--------|", + r"| | |", + r"| | |", + r"| __| |", + r"|________|"], + "Q": [r"|--------|", + r"| __ |", + r"| |__| |", + r"| \ |", + r"|________|"], + "K": [r"|--------|", + r"| | / |", + r"| |< |", + r"| | \ |", + r"|________|"], + "A": [r"|--------|", + r"| /\ |", + r"| /--\ |", + r"| | | |", + r"|________|"], + "O": [r"|--------|", # JOKER can be represented as O + r"| O |JO|", + r"| /|\ |KE|", + r"| / \ |R`|", + r"|________|"] +} + +CARD_GAP = " " +def display_cards(cards): + """Display the cards in a list of cards.""" + rows = [""] * 5 + for card in cards: + for i in range(5): + rows[i] += card_img[card][i] + CARD_GAP + for row in rows: + print(row) + + +def reset_deck(): + """重置并洗牌""" + global deck + deck = ["J"] * 4 + ["Q"] * 4 + ["K"] * 4 + ["A"] * 4 + ["O"] * 4 + random.shuffle(deck) + +def deal_cards(): + """发牌给所有活跃玩家""" + global player_hands, deck + + # 如果牌堆不够,重新洗牌 + if len(deck) < len(active_players) * 5: + reset_deck() + + # 给每个活跃玩家发5张牌 + for player in active_players: + if len(player_hands[player]) == 0: # 只给没有牌的玩家发牌 + player_hands[player] = [deck.pop() for _ in range(min(5, len(deck)))] + +def init_gun(): + """初始化左轮手枪""" + global gun_chamber, gun_index + gun_chamber = [False, False, False, False, False, True] + random.shuffle(gun_chamber) + gun_index = 0 + +def show_instructions(): + """Display the instructions for the game.""" + print('''Liars Pub, by Jack Tendy. +A simple card game for 4 players. +The goal is to live the last. +Rules: +- Each player is dealt 5 cards. +- Players take turns playing cards face down and declaring their rank (J, Q, K, A, O). +- Other players can challenge the declaration. +- If the declaration is true, the challenger gets the gun shoot. +- If the declaration is false, the declarer gets the gun shoot. +- When one player has no cards left, refill their hand from the deck. +- The gun has 6 bullets, 1 full and 5 empty.. +- When the gun is shot, if it's a full bullet, the player is out. +- The last player remaining wins the game. +- Press Enter to continue...''') + input() + + +def shoot(): + """开枪,返回是否击中""" + global gun_index, gun_chamber + + is_hit = gun_chamber[gun_index] + gun_index = (gun_index + 1) % 6 + + # 如果已经用完所有子弹,重新装填 + if gun_index == 0: + random.shuffle(gun_chamber) + + return is_hit + +def is_false_declaration(played_cards): + """检查声明是否虚假""" + global target + for card in played_cards: + if card != target and card != "O": # 如果不是目标牌也不是Joker + return True + return False + +def shoot_at(player, is_killed): + """ Draw a gun and shoot at the player. + If is_killed is True, the player is killed. + """ + print(r"~ |\ ____________ _",flush=True) + print(r"~ / |_____________||",end="",flush=True) + if is_killed: + print("■> "+player+" is killed! > x <",flush=True) + else: + print(" > "+player+" is safe! ^ _ ^",flush=True) + print(r"~ / |______________|ˉˉ",flush=True) + print(r'~ / /',flush=True) + print(r"~|_|_|",flush=True) + + + +def show_remains(remain_dict,last_player,last_action): + """Show the remaining players and their num of cards, one per row. + remain_dict is a dict of {player_name: num_of_cards}. + output should be like: + Player1: o o o o o + Player2: o o o <- if last_player is Player2 and last_action is "put out 2 cards" + Player3: (out) + if last_player and last_action are not None, + show the last action at his row of the output(Who has put out how many cards). + """ + global max_len + for player, num in remain_dict.items(): + print("# ",end="") # to flush the output buffer + if isinstance(num, list): + card_count = len(num) + else: + card_count = num + if card_count > 0: + cards = "o " * card_count + if player == last_player and last_action is not None: + print(f"{player.ljust(max_len)}: {cards} <- {last_player} {last_action}") + else: + print(f"{player.ljust(max_len)}: {cards}") + else: + print(f"{player}: (out)") + +def get_next_player(): + """获取下一个活跃玩家""" + global current_player_index, active_players + + if len(active_players) <= 1: + return current_player_index + + # 找到下一个活跃玩家 + next_index = (current_player_index + 1) % len(active_players) + return next_index + +def get_player_name(): + responses = [] + max_len = 0 + for i in range(4): + # get the player's name + name = input('#player %d \n'%(i+1)).title() + if len(name) > max_len: + max_len = len(name) + responses.append(name) + return max_len, responses + +def check_win_condition(): + """检查是否满足胜利条件""" + return len(active_players) == 1, active_players[0] if len(active_players) == 1 else None + +def get_player_input(player, hand): + """获取玩家输入(直接输入牌,不限制数量)""" + input('Turn for %s. Press Enter to continue...' % player) + print(f"\n{player}'s turn. Your cards: ") + display_cards(hand) + + print("Select cards to play (enter the card letters, separated by spaces):") + print("Available cards:", ", ".join(hand)) + + while True: + try: + selected_cards = list(input().strip().upper()) + if not selected_cards: + print("You must select at least one card.") + continue + + # 检查所选牌是否在手牌中 + valid_selection = True + temp_hand = hand.copy() + for card in selected_cards: + if card in temp_hand: + temp_hand.remove(card) + else: + print(f"Card {card} is not in your hand or has already been selected.") + valid_selection = False + break + + if valid_selection: + return selected_cards + except Exception as e: + print("Invalid input. Please try again.") + print('\n' * 50, flush=True) + +def main(): + """主函数,运行骗子酒馆游戏""" + global target, players, active_players, player_hands, current_player_index, deck, max_len + + # 显示游戏说明 + show_instructions() + + # 初始化玩家 + max_len, players = get_player_name() + active_players = players.copy() + player_hands = {player: [] for player in players} + + # 初始化牌堆和发牌 + reset_deck() + deal_cards() + + # 初始化左轮手枪 + init_gun() + + # 随机选择目标牌 + target = random.choice(["J", "Q", "K", "A"]) + print(f"\nThe target of this game is: {target}") + + # 游戏主循环 + game_over = False + winner = None + last_player = None + last_action = None + + while not game_over: + current_player = active_players[current_player_index] + + # 如果当前玩家没有牌,重新发牌 + if len(player_hands[current_player]) == 0: + deal_cards() + # 如果重新发牌后仍然没有牌,可能是牌堆空了 + if len(player_hands[current_player]) == 0: + print(f"{current_player} has no cards to play! Skipping turn.") + current_player_index = get_next_player() + continue + + # 获取玩家输入 + cards_to_play = get_player_input(current_player, player_hands[current_player]) + num_to_play = len(cards_to_play) + + # 从手牌中移除这些牌 + for card in cards_to_play: + player_hands[current_player].remove(card) + + # 玩家声明出牌 (总是声明为目标牌) + print('\n'*30, flush=True) + print(f"{current_player} plays {num_to_play} card(s) and declares: 'These are all {target}s!'") + + # 记录最后动作 + last_action = f"declared {num_to_play} {target}(s)" + last_player = current_player + + # 只有下家可以挑战 + next_player_index = get_next_player() + next_player = active_players[next_player_index] + + # 下家决定是否挑战 + challenge = input(f"{next_player}, do you challenge? (y/n): ").strip().lower() + will_challenge = challenge == 'y' + + if will_challenge: + print(f"{next_player} challenges the declaration!") + + # 验证声明 + declaration_false = is_false_declaration(cards_to_play) + + if not declaration_false: + print("The declaration was truthful!") + loser = next_player + else: + print("The declaration was a lie!") + loser = current_player + + # 执行枪击 + print(f"\nShooting at {loser}...") + time.sleep(1) # 增加 suspense + + is_dead = shoot() + shoot_at(loser, is_dead) + + # 如果被击中,移除该玩家 + if is_dead: + print(f"{loser} is eliminated from the game!") + active_players.remove(loser) + player_hands[loser] = [] + + # 检查游戏是否结束 + game_over, winner = check_win_condition() + if game_over: + break + else: + print(f"{next_player} does not challenge.") + + # 显示当前游戏状态 + print("\nCurrent game status:") + show_remains(player_hands, last_player, last_action) + + # 移动到下一个玩家 + current_player_index = get_next_player() + + # 游戏结束 + print(f"\n🎉 Game Over! {winner} wins! 🎉") + + +main() diff --git a/src/gamesbyexample/window-notificator.py b/src/gamesbyexample/window-notificator.py new file mode 100644 index 0000000..c70620c --- /dev/null +++ b/src/gamesbyexample/window-notificator.py @@ -0,0 +1,60 @@ +import random +import time + +def genWindow(): + notification = input("What notification would you like to see? ") + while len(notification) > 87: + print("Notification too long! Please keep it under 87 characters.") + notification = input("What notification would you like to see? ") + msg_length= max(30, len(notification))+4 + window = [ + '+' + '-' * (msg_length - 2) + '+', + '|' + ' ' * (msg_length - 2) + '|', + '| ' + notification.ljust(msg_length - 2) + ' |', + "|" + '[ Close Ctrl+C ]'.rjust(msg_length - 2) + " |", + '+' + '-' * (msg_length - 2) + '+' + ] + return window, msg_length + +def genRandomBg(width, height): + material = ['.','*','-']+[' ']*97 + bg = [] + for i in range(height): + line = ''.join(random.choice(material) for _ in range(width)) + bg.append(line) + return bg + +def placeWindow(x ,y, window): + global pattern + for i, line in enumerate(window): + pattern[y+i] = pattern[y+i][:x] + line + pattern[y+i][x+len(line):] + +# constants +pause = 0.1 +HEIGHT = 30 +WIDTH = 93 +window, windowWidth = genWindow() +windowHeight = len(window) +go_down, go_right = True, True +x, y = 0, 0 +while True: + pattern = genRandomBg(WIDTH, HEIGHT) + placeWindow(x, y, window) + print('\n'.join(pattern),flush=True) + time.sleep(pause) + if go_right: + x += 1 + else: + x -= 1 + if go_down: + y += 1 + else: + y -= 1 + if x + windowWidth >= WIDTH: + go_right = False + if x <= 0: + go_right = True + if y + windowHeight >= HEIGHT: + go_down = False + if y <= 0: + go_down = True \ No newline at end of file