import bitbully as bb import gradio as gr import json import hashlib ROWS = 6 COLS = 7 board = bb.Board() agent = bb.BitBully() move_history = [] confirm_reset_flag = False PASSWORD_HASH = hashlib.sha256("bitbully".encode()).hexdigest() def get_board(): grid = [["⚪" for _ in range(COLS)] for _ in range(ROWS)] turn = 1 for m in move_history: for r in range(ROWS - 1, -1, -1): if grid[r][m] == "⚪": grid[r][m] = "🔴" if turn == 1 else "🟡" break turn = 2 if turn == 1 else 1 return "\n".join([" ".join(row) for row in grid]) def export_game(): return json.dumps({"moves": "".join(str(x) for x in move_history)}, indent=2) def col_full(col): count = sum(1 for m in move_history if m == col) return count >= ROWS def play(col): global board, move_history if board.is_game_over(): return get_board(), "Game already finished", export_game() if col_full(col): return get_board(), "Column full", export_game() try: board.play(col) move_history.append(col) except: return get_board(), "Invalid move", export_game() if board.is_game_over(): return get_board(), "Game over", export_game() ai = agent.best_move(board) try: board.play(ai) move_history.append(ai) except: return get_board(), "AI move failed", export_game() if board.is_game_over(): return get_board(), f"AI played {ai} - game over", export_game() return get_board(), f"AI played {ai}", export_game() def undo(): global board, move_history if len(move_history) >= 2: move_history = move_history[:-2] board = bb.Board("".join(str(x) for x in move_history)) return get_board(), "Undone", export_game() def reset_request(): global confirm_reset_flag if not confirm_reset_flag: confirm_reset_flag = True return get_board(), "Click reset again to confirm", export_game() return reset() def reset(): global board, move_history, confirm_reset_flag board = bb.Board() move_history = [] confirm_reset_flag = False return get_board(), "Reset complete", export_game() def import_game(data): global board, move_history try: parsed = json.loads(data) moves = parsed["moves"] temp = bb.Board() validated = [] for c in moves: col = int(c) temp.play(col) validated.append(col) move_history = validated board = temp return get_board(), "Imported safely", export_game() except: return get_board(), "Import failed", export_game() with gr.Blocks() as app: gr.Markdown("# BitBully Connect 4 Admin GUI") board_display = gr.Textbox(value=get_board(), lines=8, label="Board") status = gr.Textbox(value="Ready") export_box = gr.Textbox(value=export_game(), lines=5, label="Import / Export") with gr.Row(): for i in range(7): gr.Button(f"Drop {i}").click( lambda x=i: play(x), outputs=[board_display, status, export_box] ) with gr.Row(): gr.Button("Undo").click(undo, outputs=[board_display, status, export_box]) gr.Button("Reset").click(reset_request, outputs=[board_display, status, export_box]) gr.Button("Import").click(import_game, inputs=[export_box], outputs=[board_display, status, export_box]) app.launch()