Skip to content

Commit

Permalink
add tournaments
Browse files Browse the repository at this point in the history
  • Loading branch information
Kagwep committed Sep 2, 2024
1 parent c333b68 commit e824ff0
Show file tree
Hide file tree
Showing 18 changed files with 1,257 additions and 22 deletions.
16 changes: 11 additions & 5 deletions oware-dapp/game_play/challenge.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@
import tflite_runtime.interpreter as tflite
from .movesevaluator import GameplayEvaluationMoves
import logging

from pathlib import Path
from .leaderboard import Leaderboard



Expand All @@ -20,6 +21,8 @@

sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))

leaderboard = Leaderboard()

OPlayer = namedtuple('Player', ['name', 'address', 'model_name'])

class Challenge:
Expand Down Expand Up @@ -66,11 +69,11 @@ def spawn(self):
self.player_one = Player(self.creator.name,PLAYER_ONE_HOUSES,0,self.creator.address)
self.player_two = Player(self.opponent.name,PLAYER_TWO_HOUSES,0,self.opponent.address)
self.model_player_one = self.load_model_tflite(self.creator.model_name) if self.creator.model_name else None
self.model_player_two = self.load_model_tflite(self.opponent.model_name) if self.creator.model_name else None
self.model_player_two = self.load_model_tflite(self.opponent.model_name) if self.opponent.model_name else None
else:
self.player_one = Player(self.opponent.name,PLAYER_ONE_HOUSES,0,self.opponent.address)
self.player_two = Player(self.creator.name,PLAYER_TWO_HOUSES,0,self.creator.address)
self.model_player_two = self.load_model_tflite(self.creator.model_name) if self.creator.model_name else None
self.model_player_two = self.load_model_tflite(self.creator.model_name) if self.opponent.model_name else None
self.model_player_one = self.load_model_tflite(self.opponent.model_name) if self.creator.model_name else None

self.turn = self.player_one
Expand Down Expand Up @@ -178,20 +181,23 @@ def move(self,selected_house):
}

def load_model_tflite(self,name):
model = tflite.Interpreter(model_path=f"./models-tflite/agent-model-new-{name}.tflite")
model_path = os.path.join(Path(__file__).parent, f'models-tflite/{name}.tflite')
model = tflite.Interpreter(model_path=model_path)
return model

def update_round_winner(self, result):
"""Update the round winner based on the game result."""
if result == 1:
winner = self.player_one.get_player()
self.player_one_wins += 1
self.round_winners[self.current_round] = winner
leaderboard.add_or_update_player(self.player_one.name, self.player_one.player_address, score=100)
return True
elif result == 2:
winner = self.player_two.get_player()
self.player_two_wins += 1
self.round_winners[self.current_round] = winner
leaderboard.add_or_update_player(self.player_two.name, self.player_two.player_address, score=20)
return True
else:
if self.game.state.inprogress:
Expand Down
5 changes: 4 additions & 1 deletion oware-dapp/game_play/game/constants.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import json
import os
import pathlib

NUMBER_OF_HOUSES = 12
PLAYER_ONE_HOUSES = ["House1", "House2", "House3", "House4", "House5", "House6"]
Expand All @@ -11,8 +13,9 @@

def load_model_addresses():
"""Load model addresses from the JSON file."""
new_path = os.path.join(pathlib.Path(__file__).parent.resolve(), 'model_addresses.json')
try:
with open('model_addresses.json', 'r') as f:
with open(new_path, 'r') as f:
return json.load(f)
except FileNotFoundError:
return {}
Expand Down
10 changes: 7 additions & 3 deletions oware-dapp/game_play/leaderboard.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,14 @@ def __init__(self):
# Store player details using Ethereum address as a key
self.players = {}

def add_player(self, player_name, eth_address):
# Add a new player with an Ethereum address
def add_or_update_player(self, player_name, eth_address, score=0):
# Add a new player or update an existing player's score
if eth_address not in self.players:
self.players[eth_address] = {'player_name': player_name, 'score': 0, 'rank_title': None}
self.players[eth_address] = {'player_name': player_name, 'score': score, 'rank_title': None}
else:
self.players[eth_address]['score'] += score

self.update_rank_title(eth_address)

def update_score(self, eth_address, score):
# Update the score for a player and re-calculate rank titles
Expand Down
12 changes: 12 additions & 0 deletions oware-dapp/game_play/test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import os
import pathlib
import tflite_runtime.interpreter as tflite
from pathlib import Path


def load_model_tflite():
model_path = os.path.join(Path(__file__).parent, f'models-tflite/Bao.tflite')
model = tflite.Interpreter(model_path=model_path)
return model

load_model_tflite()
16 changes: 15 additions & 1 deletion oware-dapp/game_play/tournaments.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import time
from collections import namedtuple
from .challenge import Challenge
from game_play.game.constants import MODEL_ADDRESSES

# Define a Player named tuple to store player attributes
OPlayer = namedtuple('Player', ['name', 'address', 'model_name'])
Expand All @@ -24,6 +25,7 @@ def __init__(self,creator, number_of_players, rounds_per_challenge):
self.fixtures = {}
self.active_round = 1
self.round_winners = {}
self.in_progress = False

if not self.is_valid_player_count(self.max_players):
raise ValueError(f"Invalid number of players. Must be an even number between 4 and 16 inclusive.")
Expand All @@ -39,6 +41,7 @@ def join_tournament(self,player):
def start(self):
self.started_at = time.time()
self.create_fixtures(self.players)
self.in_progress = True

def create_fixtures(self, players):
""" Randomly pair players and create fixtures for a round of challenges. """
Expand All @@ -49,7 +52,15 @@ def create_fixtures(self, players):
challenge_id = self.next_challenge_id
player_one = players[i]
player_two = players[i + 1]
new_challenge = Challenge(player_one.name, player_one.address,self.rounds_per_challenge,challenge_id,player_one.model_name)

challenge_type = 1

if (player_one.address == MODEL_ADDRESSES[player_one.name]) and (player_two.address == MODEL_ADDRESSES[player_two.name]):
challenge_type = 3
elif((player_one.address == MODEL_ADDRESSES[player_one.name]) or (player_two.address == MODEL_ADDRESSES[player_two.name])):
challenge_type = 2

new_challenge = Challenge(player_one.name, player_one.address,self.rounds_per_challenge,challenge_type,challenge_id,player_one.model_name)
new_challenge.add_opponent(player_two.name, player_two.address, player_two.model_name)
new_challenge.spawn()
self.challenges[challenge_id] = new_challenge
Expand All @@ -74,6 +85,8 @@ def update_tournament_state(self, challenge_id, winner):
"""Check if all challenges in the current round are completed."""
if self.is_round_complete():
self.prepare_next_round()



def is_round_complete(self):
"""Check if all challenges in the current round are completed."""
Expand All @@ -87,6 +100,7 @@ def prepare_next_round(self):
if len(winners) == 1:
self.tournament_winner = winners[0]
self.ended_at = time.time()
self.in_progress = False
return

self.active_round += 1
Expand Down
213 changes: 213 additions & 0 deletions oware-dapp/oware-frontend/src/components/ListTournaments.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,213 @@
import React, { useState } from 'react';
import { Tournament } from '../utils/types';
import { Box, Text, VStack, HStack, Badge, Heading, useColorModeValue, Button, useToast, SimpleGrid, Flex, Tooltip, List } from '@chakra-ui/react';
import { CheckCircleIcon, TimeIcon, StarIcon, InfoIcon } from '@chakra-ui/icons';
import JoinTournamentFormModal from './forms/JoinTournamentForm';
import { useAccount } from 'wagmi';
import { v4 as uuidv4 } from 'uuid';
import { sendInput } from '../utils';
import { useWriteInputBoxAddInput } from "../hooks/generated";
import Arena from '../pages/Arena';
import { fetchGraphQLData } from '../utils/api';
import { NOTICES_QUERY } from '../utils/query';
import { useTournaments } from '../hooks/useTournaments';
import AddOpponentTournamentFormModal from './forms/AddOpponentTournamentFormModal';
import ListTournamentChallenges from './TouramentChallenges';

interface ListTournamentsProps {
tournaments: Tournament[];
onJoinTournament: (data: any) => void;
fetchTournaments: () => Promise<void>;
onAddOpponentTournament : (data: any) => void;
}

const ListTournaments: React.FC<ListTournamentsProps> = ({ tournaments, onJoinTournament, fetchTournaments,onAddOpponentTournament}) => {
const bgColor = useColorModeValue('gray.900', 'gray.700');
const borderColor = useColorModeValue('gray.200', 'gray.600');
const { address, isConnected } = useAccount()
const toast = useToast();
const [isOpen, setIsOpen] = useState(false);
const [isOpenOne, setIsOpenOne] = useState(false);
const [selectedTournamentId, setSelectedTournamentId] = useState<string | null>(null);
const [selectedTournament, setSelectedTournament] = useState<Tournament | null>(null);

const {writeContractAsync} = useWriteInputBoxAddInput()

const handleJoinClick = (tournamentId: string) => {
if (!isConnected) {
toast({
title: 'Wallet not connected',
description: 'Please connect your wallet to proceed.',
status: 'warning',
duration: 5000,
isClosable: true,
});
} else {
setSelectedTournamentId(tournamentId);
setIsOpen(true);
}
};

const handleOpponentAddClick= (tournamentId: string) => {
if (!isConnected) {
toast({
title: 'Wallet not connected',
description: 'Please connect your wallet to proceed.',
status: 'warning',
duration: 5000,
isClosable: true,
});
} else {
setSelectedTournamentId(tournamentId);
setIsOpenOne(true);
}
};

const handleGoToChallenges = (tournament: Tournament) => {
setSelectedTournament(tournament);
};


if (selectedTournament) {

return <ListTournamentChallenges tournament={selectedTournament} fetchTournaments={fetchTournaments} />;
}


const isCreator = (creatorAddress: string) => {
return address && creatorAddress.toLowerCase() === address.toLowerCase();
};

return (
<VStack spacing={4} align="stretch" w="full" maxW="800px" mx="auto">
<Heading size="xl" mb={6} textAlign="center">Tournaments</Heading>
{tournaments.length > 0 ? (
tournaments.map((tournament: Tournament) => (
<Box
key={tournament.tournament_id}
p={5}
borderWidth="1px"
borderRadius="lg"
borderColor={borderColor}
bg={bgColor}
boxShadow="md"
transition="all 0.3s"
_hover={{ transform: 'translateY(-2px)', boxShadow: 'lg' }}
>
<HStack justifyContent="space-between" mb={3}>
<Badge bg="green.500" fontSize="0.8em" p={1} rounded={'5'}>
Tournament: {tournament.tournament_id}
</Badge>
<HStack>
{tournament.in_progress ? (
<Badge colorScheme="green"><TimeIcon mr={1} />In Progress</Badge>
) : tournament.ended_at? (
<Badge colorScheme="red"><CheckCircleIcon mr={1} />Ended</Badge>
) : tournament.players.length == tournament.no_of_players ? (
<Badge bg="yellow.600">Ready to Start</Badge>
) : (
<Badge bg="yellow.600">Waiting for Opponent</Badge>
)}
</HStack>
</HStack>
<HStack justifyContent="space-between" mb={2}>
<VStack align="start" spacing={1}>
<Text fontWeight="bold">Creator:</Text>
<Tooltip label={tournament.creator} placement="bottom">
<Text color="cyan.200" isTruncated maxW="150px">
{tournament.creator}
</Text>
</Tooltip>
</VStack>
<VStack align="end" spacing={1}>
<Text fontWeight="bold">Players:</Text>
{tournament.players.length > 0 ? (
tournament.players.map((player, index) => (
<Tooltip
key={index}
label={`Address: ${player.address}${player.model_name ? `\nAgent: ${player.model_name}` : ''}`}
placement="bottom"
>
<HStack spacing={1}>
<Text color="teal.200" isTruncated maxW="120px">
{player.name}
</Text>
{player.model_name && (
<Badge colorScheme="purple" fontSize="xs">
AI
</Badge>
)}
<InfoIcon boxSize={3} color="gray.400" />
</HStack>
</Tooltip>
))
) : (
<Text color="teal.200">No players</Text>
)}
</VStack>
</HStack>

{tournament.creator.toLocaleLowerCase() == address?.toLowerCase() ? (
<Button
bg="orange.900"
size="sm"
color={'white'}
leftIcon={<StarIcon />}
onClick={() => handleOpponentAddClick(tournament.tournament_id)}
mt={2}
mb={2}
>
Add opponent
</Button>
):(
<Button
bg="purple.600"
size="sm"
color={'white'}
onClick={() => handleJoinClick(tournament.tournament_id)}
>
Join Tournament
</Button>
)}

{tournament.in_progress && (
<Button
bg="orange.900"
size="sm"
color={'white'}
onClick={() => handleGoToChallenges(tournament)}
mt={2}
mb={2}
>
Go to Arena
</Button>
)}
<Text mb={2}>Winner: <span className='text-cyan-500'>{tournament.winner?.address || 'N/A'}</span></Text>

<Text fontSize="sm" color="gray.500" mt={2}>
Created at: {new Date(tournament.started_at * 1000).toLocaleString()}
</Text>
<JoinTournamentFormModal
isOpen={isOpen && selectedTournamentId === tournament.tournament_id}
onClose={() => setIsOpen(false)}
onJoinTournament={onJoinTournament}
tournamentId={tournament.tournament_id}
/>
<AddOpponentTournamentFormModal
isOpen={isOpenOne && selectedTournamentId === tournament.tournament_id}
onClose={() => setIsOpenOne(false)}
onAddOpponentTournament={onAddOpponentTournament}
tournamentId={tournament.tournament_id}
/>
</Box>
))
) : (
<Box textAlign="center" p={5} borderWidth="1px" borderRadius="lg" borderColor={borderColor}>
<Text fontSize="lg">No Tournaments available at the moment.</Text>
</Box>
)}
</VStack>
);
};

export default ListTournaments;
Loading

0 comments on commit e824ff0

Please sign in to comment.