Removed module projects because it can't work like that atm. Commented out package commands.

This commit is contained in:
Master Kwoth
2017-10-15 09:39:46 +02:00
parent 90e71a3a30
commit 696a0eb2a7
180 changed files with 21625 additions and 1058 deletions
NadekoBot.Core
Migrations
Modules
Administration
Gambling
Games
Music
Nsfw
Pokemon
Searches
Utility
Xp
NadekoBot.Core.csproj
Services
NadekoBot.Modules.Games
NadekoBot.sln
docs
mkdocs.yml
src/NadekoBot

@ -0,0 +1,181 @@
using NadekoBot.Common;
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
namespace NadekoBot.Modules.Games.Common.Nunchi
{
public class Nunchi : IDisposable
{
public enum Phase
{
Joining,
Playing,
WaitingForNextRound,
Ended,
}
public int CurrentNumber { get; private set; } = new NadekoRandom().Next(0, 100);
public Phase CurrentPhase { get; private set; } = Phase.Joining;
public event Func<Nunchi, Task> OnGameStarted;
public event Func<Nunchi, int, Task> OnRoundStarted;
public event Func<Nunchi, Task> OnUserGuessed;
public event Func<Nunchi, (ulong Id, string Name)?, Task> OnRoundEnded; // tuple of the user who failed
public event Func<Nunchi, string, Task> OnGameEnded; // name of the user who won
private readonly SemaphoreSlim _locker = new SemaphoreSlim(1, 1);
private HashSet<(ulong Id, string Name)> _participants = new HashSet<(ulong Id, string Name)>();
private HashSet<(ulong Id, string Name)> _passed = new HashSet<(ulong Id, string Name)>();
public ImmutableArray<(ulong Id, string Name)> Participants => _participants.ToImmutableArray();
public int ParticipantCount => _participants.Count;
private const int _killTimeout = 20 * 1000;
private const int _nextRoundTimeout = 5 * 1000;
private Timer _killTimer;
public Nunchi(ulong creatorId, string creatorName)
{
_participants.Add((creatorId, creatorName));
}
public async Task<bool> Join(ulong userId, string userName)
{
await _locker.WaitAsync().ConfigureAwait(false);
try
{
if (CurrentPhase != Phase.Joining)
return false;
return _participants.Add((userId, userName));
}
finally { _locker.Release(); }
}
public async Task<bool> Initialize()
{
CurrentPhase = Phase.Joining;
await Task.Delay(30000).ConfigureAwait(false);
await _locker.WaitAsync().ConfigureAwait(false);
try
{
if (_participants.Count < 3)
{
CurrentPhase = Phase.Ended;
return false;
}
_killTimer = new Timer(async state =>
{
await _locker.WaitAsync().ConfigureAwait(false);
try
{
if (CurrentPhase != Phase.Playing)
return;
//if some players took too long to type a number, boot them all out and start a new round
_participants = new HashSet<(ulong, string)>(_passed);
EndRound();
}
finally { _locker.Release(); }
}, null, _killTimeout, _killTimeout);
CurrentPhase = Phase.Playing;
var _ = OnGameStarted?.Invoke(this);
var __ = OnRoundStarted?.Invoke(this, CurrentNumber);
return true;
}
finally { _locker.Release(); }
}
public async Task Input(ulong userId, string userName, int input)
{
await _locker.WaitAsync().ConfigureAwait(false);
try
{
if (CurrentPhase != Phase.Playing)
return;
var userTuple = (Id: userId, Name: userName);
// if the user is not a member of the race,
// or he already successfully typed the number
// ignore the input
if (!_participants.Contains(userTuple) || !_passed.Add(userTuple))
return;
//if the number is correct
if (CurrentNumber == input - 1)
{
//increment current number
++CurrentNumber;
if (_passed.Count == _participants.Count - 1)
{
// if only n players are left, and n - 1 type the correct number, round is over
// if only 2 players are left, game is over
if (_participants.Count == 2)
{
_killTimer.Change(Timeout.Infinite, Timeout.Infinite);
CurrentPhase = Phase.Ended;
var _ = OnGameEnded?.Invoke(this, userTuple.Name);
}
else // else just start the new round without the user who was the last
{
var failure = _participants.Except(_passed).First();
EndRound(failure);
}
}
var __ = OnUserGuessed?.Invoke(this);
}
else
{
//if the user failed
EndRound(userTuple);
}
}
finally { _locker.Release(); }
}
private void EndRound((ulong, string)? failure = null)
{
_killTimer.Change(_killTimeout, _killTimeout);
CurrentNumber = new NadekoRandom().Next(0, 100); // reset the counter
_passed.Clear(); // reset all users who passed (new round starts)
if(failure != null)
_participants.Remove(failure.Value); // remove the dude who failed from the list of players
var __ = OnRoundEnded?.Invoke(this, failure);
if (_participants.Count <= 1) // means we have a winner or everyone was booted out
{
_killTimer.Change(Timeout.Infinite, Timeout.Infinite);
CurrentPhase = Phase.Ended;
var _ = OnGameEnded?.Invoke(this, _participants.Count > 0 ? _participants.First().Name : null);
return;
}
CurrentPhase = Phase.WaitingForNextRound;
var throwawayDelay = Task.Run(async () =>
{
await Task.Delay(_nextRoundTimeout).ConfigureAwait(false);
CurrentPhase = Phase.Playing;
var ___ = OnRoundStarted?.Invoke(this, CurrentNumber);
});
}
public void Dispose()
{
OnGameEnded = null;
OnGameStarted = null;
OnRoundEnded = null;
OnRoundStarted = null;
OnUserGuessed = null;
}
}
}