-
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Fixed ordering of the scoreboard. People with faster solves will be higher. - Added point adjustments for new baseline submissions. - Implemented fixed ordering for CTFtime endpoints. - Fixed colours on team detail page.
- Loading branch information
Showing
19 changed files
with
386 additions
and
21 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
// This file is part of RosettaCTF project. | ||
// | ||
// Copyright 2020 Emzi0767 | ||
// | ||
// Licensed under the Apache License, Version 2.0 (the "License"); | ||
// you may not use this file except in compliance with the License. | ||
// You may obtain a copy of the License at | ||
// | ||
// http://www.apache.org/licenses/LICENSE-2.0 | ||
// | ||
// Unless required by applicable law or agreed to in writing, software | ||
// distributed under the License is distributed on an "AS IS" BASIS, | ||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
// See the License for the specific language governing permissions and | ||
// limitations under the License. | ||
|
||
namespace RosettaCTF.Models | ||
{ | ||
public struct ScoreInfo | ||
{ | ||
public int Current { get; } | ||
public int Next { get; } | ||
|
||
public ScoreInfo(int current, int next) | ||
{ | ||
this.Current = current; | ||
this.Next = next; | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
// This file is part of RosettaCTF project. | ||
// | ||
// Copyright 2020 Emzi0767 | ||
// | ||
// Licensed under the Apache License, Version 2.0 (the "License"); | ||
// you may not use this file except in compliance with the License. | ||
// You may obtain a copy of the License at | ||
// | ||
// http://www.apache.org/licenses/LICENSE-2.0 | ||
// | ||
// Unless required by applicable law or agreed to in writing, software | ||
// distributed under the License is distributed on an "AS IS" BASIS, | ||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
// See the License for the specific language governing permissions and | ||
// limitations under the License. | ||
|
||
using System; | ||
using System.Threading; | ||
|
||
namespace RosettaCTF.Models | ||
{ | ||
public sealed class ScoreLock : IDisposable | ||
{ | ||
private SemaphoreSlim Semaphore { get; } | ||
|
||
public ScoreLock(SemaphoreSlim semaphore) | ||
{ | ||
this.Semaphore = semaphore; | ||
} | ||
|
||
public void Dispose() | ||
{ | ||
this.Semaphore.Release(1); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,143 @@ | ||
// This file is part of RosettaCTF project. | ||
// | ||
// Copyright 2020 Emzi0767 | ||
// | ||
// Licensed under the Apache License, Version 2.0 (the "License"); | ||
// you may not use this file except in compliance with the License. | ||
// You may obtain a copy of the License at | ||
// | ||
// http://www.apache.org/licenses/LICENSE-2.0 | ||
// | ||
// Unless required by applicable law or agreed to in writing, software | ||
// distributed under the License is distributed on an "AS IS" BASIS, | ||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
// See the License for the specific language governing permissions and | ||
// limitations under the License. | ||
|
||
using System; | ||
using System.Collections.Generic; | ||
using System.Linq; | ||
using System.Threading; | ||
using System.Threading.Tasks; | ||
using RosettaCTF.Data; | ||
using RosettaCTF.Models; | ||
|
||
namespace RosettaCTF.Services | ||
{ | ||
/// <summary> | ||
/// Handles score computations. | ||
/// </summary> | ||
public sealed class ScoreCalculatorService | ||
{ | ||
private ICtfChallengeRepository ChallengeRepository { get; } | ||
private ICtfChallengeCacheRepository ChallengeCacheRepository { get; } | ||
private IScoringModel ScoringModel { get; } | ||
private ScoreLockService ScoreLockService { get; } | ||
|
||
public ScoreCalculatorService( | ||
ICtfChallengeRepository ctfChallengeRepository, | ||
ICtfChallengeCacheRepository ctfChallengeCacheRepository, | ||
IScoringModel scoringModel, | ||
ScoreLockService scoreLockService) | ||
{ | ||
this.ChallengeRepository = ctfChallengeRepository; | ||
this.ChallengeCacheRepository = ctfChallengeCacheRepository; | ||
this.ScoringModel = scoringModel; | ||
this.ScoreLockService = scoreLockService; | ||
} | ||
|
||
public async Task<ScoreInfo> ComputeCurrentScoreAsync(ICtfChallenge challenge, CancellationToken cancellationToken = default) | ||
{ | ||
using var _ = await this.ScoreLockService.AcquireLockAsync(challenge.Id, cancellationToken); | ||
|
||
var solves = await this.ChallengeCacheRepository.IncrementSolveCountAsync(challenge.Id, cancellationToken); | ||
var baseline = await this.ChallengeCacheRepository.GetBaselineSolveCountAsync(cancellationToken); | ||
var rate = solves / (double)baseline; | ||
var postRate = (solves + 1.0) / baseline; | ||
var cscore = this.ScoringModel.ComputeScore(challenge.BaseScore, rate); | ||
var pscore = this.ScoringModel.ComputeScore(challenge.BaseScore, postRate); | ||
|
||
await this.ChallengeCacheRepository.UpdateScoreAsync(challenge.Id, pscore, cancellationToken); | ||
return new ScoreInfo(cscore, pscore); | ||
} | ||
|
||
public async Task UpdateAllScoresAsync(bool freezeScores, bool includeBaseline, CancellationToken cancellationToken = default) | ||
{ | ||
var baseline = includeBaseline | ||
? await this.RecountBaselineAsync(cancellationToken) | ||
: await this.ChallengeCacheRepository.GetBaselineSolveCountAsync(cancellationToken); | ||
|
||
if (includeBaseline) | ||
await this.ChallengeCacheRepository.SetBaselineSolveCountAsync(baseline, cancellationToken); | ||
|
||
await this.UpdateAllScoresJeopardyAsync(baseline, cancellationToken); | ||
if (freezeScores) | ||
await this.UpdateAllScoresFreezerAsync(baseline, cancellationToken); | ||
} | ||
|
||
private async Task<int> RecountBaselineAsync(CancellationToken cancellationToken = default) | ||
{ | ||
var solves = await this.ChallengeRepository.GetSuccessfulSolvesAsync(cancellationToken); | ||
return solves.Count(x => x.Challenge.BaseScore <= 1); | ||
} | ||
|
||
private async Task UpdateAllScoresJeopardyAsync(double baseline, CancellationToken cancellationToken = default) | ||
{ | ||
var challenges = await this.ChallengeRepository.GetChallengesAsync(cancellationToken); | ||
challenges = challenges.Where(x => x.BaseScore > 1); | ||
var locks = await Task.WhenAll(challenges.Select(x => this.ScoreLockService.AcquireLockAsync(x.Id, cancellationToken))); | ||
|
||
try | ||
{ | ||
foreach (var challenge in challenges) | ||
{ | ||
var solves = await this.ChallengeCacheRepository.GetSolveCountAsync(challenge.Id, cancellationToken); | ||
var rate = solves / baseline; | ||
var score = this.ScoringModel.ComputeScore(challenge.BaseScore, rate); | ||
await this.ChallengeCacheRepository.UpdateScoreAsync(challenge.Id, score, cancellationToken); | ||
} | ||
} | ||
finally | ||
{ | ||
foreach (var @lock in locks) | ||
@lock.Dispose(); | ||
} | ||
} | ||
|
||
private async Task UpdateAllScoresFreezerAsync(double baseline, CancellationToken cancellationToken = default) | ||
{ | ||
var solves = await this.ChallengeRepository.GetSuccessfulSolvesAsync(cancellationToken); | ||
solves.Where(x => x.Challenge.BaseScore > 1); | ||
var locks = await Task.WhenAll(solves.Select(x => x.Challenge.Id) | ||
.Distinct(StringComparer.OrdinalIgnoreCase) | ||
.Select(x => this.ScoreLockService.AcquireLockAsync(x, cancellationToken))); | ||
|
||
try | ||
{ | ||
var updates = this.CreateUpdates(solves, baseline); | ||
await this.ChallengeRepository.UpdateSolvesAsync(updates, cancellationToken); | ||
|
||
|
||
} | ||
finally | ||
{ | ||
foreach (var @lock in locks) | ||
@lock.Dispose(); | ||
} | ||
} | ||
|
||
private IEnumerable<CtfSolveUpdate> CreateUpdates(IEnumerable<ICtfSolveSubmission> solves, double baseline) | ||
{ | ||
foreach (var solveGroup in solves.GroupBy(x => x.Challenge.Id, StringComparer.OrdinalIgnoreCase)) | ||
{ | ||
var i = 0; | ||
foreach (var solve in solveGroup) | ||
{ | ||
var rate = i++ / baseline; | ||
var score = this.ScoringModel.ComputeScore(solve.Challenge.BaseScore, rate); | ||
yield return new CtfSolveUpdate(solve, score); | ||
} | ||
} | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
// This file is part of RosettaCTF project. | ||
// | ||
// Copyright 2020 Emzi0767 | ||
// | ||
// Licensed under the Apache License, Version 2.0 (the "License"); | ||
// you may not use this file except in compliance with the License. | ||
// You may obtain a copy of the License at | ||
// | ||
// http://www.apache.org/licenses/LICENSE-2.0 | ||
// | ||
// Unless required by applicable law or agreed to in writing, software | ||
// distributed under the License is distributed on an "AS IS" BASIS, | ||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
// See the License for the specific language governing permissions and | ||
// limitations under the License. | ||
|
||
using System; | ||
using System.Collections.Concurrent; | ||
using System.Threading; | ||
using System.Threading.Tasks; | ||
using RosettaCTF.Models; | ||
|
||
namespace RosettaCTF.Services | ||
{ | ||
public sealed class ScoreLockService | ||
{ | ||
private ConcurrentDictionary<string, SemaphoreSlim> ScoreSemaphores { get; } | ||
|
||
public ScoreLockService() | ||
{ | ||
this.ScoreSemaphores = new ConcurrentDictionary<string, SemaphoreSlim>(StringComparer.OrdinalIgnoreCase); | ||
} | ||
|
||
public async Task<ScoreLock> AcquireLockAsync(string challengeId, CancellationToken cancellationToken = default) | ||
{ | ||
if (!this.ScoreSemaphores.TryGetValue(challengeId, out var semaphore)) | ||
semaphore = this.ScoreSemaphores.GetOrAdd(challengeId, new SemaphoreSlim(1, 1)); | ||
|
||
await semaphore.WaitAsync(cancellationToken); | ||
return new ScoreLock(semaphore); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.