Skip to content

Commit 52f9bb1

Browse files
committed
Add achievements for solving beta and post mortem challenges
1 parent 522f256 commit 52f9bb1

File tree

5 files changed

+138
-70
lines changed

5 files changed

+138
-70
lines changed

.sqlx/query-6266c00c38abe96ade39599e6a3e404642db017c463b983c0b80129c391c7aae.json renamed to .sqlx/query-3f3cae89fbc4a183706c980f1ca06662d657e65b0a375c0e2f9d84580d620e5c.json

Lines changed: 9 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

.sqlx/query-65573a28d2159c08521322a724c0f3c241c36da4d6b6772a84d55aa3ef7e954b.json renamed to .sqlx/query-fe01bb71e3092dbefa15eff781bb70dd635627c5589862c7a74d32c7f51c4444.json

Lines changed: 8 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

common/src/achievements.rs

Lines changed: 48 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -30,10 +30,13 @@ pub enum AchievementType {
3030
UncontestedFirstPlace,
3131
FirstPlace,
3232
OnlySolution,
33+
UncontestedFirstPlacePostMortem,
3334
// FiveLanguages,
3435
// ImproveFirstPlace,
3536
FirstDaySolve,
3637
LastDaySolve,
38+
SolveBeta,
39+
SolvePostMortem,
3740
// Change Suggestion Related
3841
ImproveDescription,
3942
ImproveJudge,
@@ -105,6 +108,9 @@ impl AchievementType {
105108
AchievementType::FirstPlace => "A winner is you",
106109
AchievementType::UncontestedFirstPlace => "The winner you are",
107110
AchievementType::OnlySolution => "A player be thee",
111+
AchievementType::SolveBeta => "Lab Rat",
112+
AchievementType::UncontestedFirstPlacePostMortem => "It's not over till it's over",
113+
AchievementType::SolvePostMortem => "Coroner",
108114
AchievementType::ImproveDescription => "Pedant",
109115
AchievementType::ImproveExample => "[Insert Name Here]",
110116
AchievementType::ImproveJudge => "[Insert Name Here]",
@@ -129,7 +135,10 @@ impl AchievementType {
129135
| AchievementType::LastDaySolve
130136
| AchievementType::FirstPlace
131137
| AchievementType::OnlySolution
132-
| AchievementType::UncontestedFirstPlace => AchievementCategory::SolveRelated,
138+
| AchievementType::UncontestedFirstPlace
139+
| AchievementType::SolveBeta
140+
| AchievementType::SolvePostMortem
141+
| AchievementType::UncontestedFirstPlacePostMortem => AchievementCategory::SolveRelated,
133142
AchievementType::ImproveExample
134143
| AchievementType::ImproveJudge
135144
| AchievementType::ImproveDescription
@@ -185,6 +194,11 @@ impl AchievementType {
185194
"Solve a challenge within 24 hours of when it goes live"
186195
}
187196
AchievementType::LastDaySolve => "Solve a challenge less than 24 hours before it ends",
197+
AchievementType::SolveBeta => "Solve a beta challenge",
198+
AchievementType::SolvePostMortem => "Solve a challenge after it ends",
199+
AchievementType::UncontestedFirstPlacePostMortem => {
200+
"Beat the best score after the challenge has ended"
201+
}
188202
AchievementType::Contribute => "Contribute to Byte Heist",
189203
AchievementType::FirstPlace => {
190204
"Get first place on a challenge, even if just for a moment"
@@ -205,6 +219,39 @@ impl AchievementType {
205219
}
206220
}
207221

222+
pub fn get_associated_language(&self) -> Option<&'static Lang> {
223+
match self {
224+
AchievementType::Apl1000Point => Some("tinyapl"),
225+
AchievementType::Python1000Point => Some("python"),
226+
AchievementType::C1000Point => Some("tcc"),
227+
AchievementType::Rust1000Point => Some("rust"),
228+
AchievementType::Vyxal1000Point => Some("vyxal3"),
229+
AchievementType::JavaScript1000Point | Self::JavaScript3500Point => Some("nodejs"),
230+
_ => None,
231+
}
232+
.and_then(|i| LANGS.get(i))
233+
}
234+
235+
pub fn get_associated_category(&self) -> Option<ChallengeCategory> {
236+
match self {
237+
AchievementType::CodeGolf1000Point
238+
| AchievementType::CodeGolf1Point
239+
| AchievementType::CodeGolf250Point
240+
| AchievementType::CodeGolf2000Point
241+
| AchievementType::CodeGolf500Point => Some(ChallengeCategory::CodeGolf),
242+
243+
AchievementType::RestrictedSource1000Point
244+
| AchievementType::RestrictedSource1Point
245+
| AchievementType::RestrictedSource250Point
246+
| AchievementType::RestrictedSource2000Point
247+
| AchievementType::RestrictedSource500Point => {
248+
Some(ChallengeCategory::RestrictedSource)
249+
}
250+
251+
_ => None,
252+
}
253+
}
254+
208255
pub fn get_icon(self) -> String {
209256
format!("<img src=\"/static/achievement-icons/{self:?}.svg\">")
210257
}
@@ -333,37 +380,4 @@ impl AchievementType {
333380
}
334381
)
335382
}
336-
337-
pub fn get_associated_language(&self) -> Option<&'static Lang> {
338-
match self {
339-
AchievementType::Apl1000Point => Some("tinyapl"),
340-
AchievementType::Python1000Point => Some("python"),
341-
AchievementType::C1000Point => Some("tcc"),
342-
AchievementType::Rust1000Point => Some("rust"),
343-
AchievementType::Vyxal1000Point => Some("vyxal3"),
344-
AchievementType::JavaScript1000Point | Self::JavaScript3500Point => Some("nodejs"),
345-
_ => None,
346-
}
347-
.and_then(|i| LANGS.get(i))
348-
}
349-
350-
pub fn get_associated_category(&self) -> Option<ChallengeCategory> {
351-
match self {
352-
AchievementType::CodeGolf1000Point
353-
| AchievementType::CodeGolf1Point
354-
| AchievementType::CodeGolf250Point
355-
| AchievementType::CodeGolf2000Point
356-
| AchievementType::CodeGolf500Point => Some(ChallengeCategory::CodeGolf),
357-
358-
AchievementType::RestrictedSource1000Point
359-
| AchievementType::RestrictedSource1Point
360-
| AchievementType::RestrictedSource250Point
361-
| AchievementType::RestrictedSource2000Point
362-
| AchievementType::RestrictedSource500Point => {
363-
Some(ChallengeCategory::RestrictedSource)
364-
}
365-
366-
_ => None,
367-
}
368-
}
369383
}

main-server/src/discord.rs

Lines changed: 68 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -288,43 +288,68 @@ async fn award_achievements(
288288
top_solution: &Option<LeaderboardEntry>,
289289
solution: &SolutionWithLanguage,
290290
) -> Result<(), sqlx::Error> {
291-
if top_solution.is_none() {
291+
if solution.is_post_mortem {
292292
award_achievement(
293293
pool,
294294
solution.author,
295-
AchievementType::OnlySolution,
295+
AchievementType::SolvePostMortem,
296296
Some(challenge_id),
297297
Some(&solution.language),
298298
)
299299
.await?;
300-
}
301300

302-
if top_solution
303-
.as_ref()
304-
.is_none_or(|e| e.points <= solution.points)
305-
{
306-
award_achievement(
307-
pool,
308-
solution.author,
309-
AchievementType::FirstPlace,
310-
Some(challenge_id),
311-
Some(&solution.language),
312-
)
313-
.await?;
314-
}
301+
if top_solution
302+
.as_ref()
303+
.is_none_or(|e| e.author_id == solution.author && e.is_post_mortem)
304+
{
305+
award_achievement(
306+
pool,
307+
solution.author,
308+
AchievementType::SolvePostMortem,
309+
Some(challenge_id),
310+
Some(&solution.language),
311+
)
312+
.await?;
313+
}
314+
} else {
315+
if top_solution.is_none() {
316+
award_achievement(
317+
pool,
318+
solution.author,
319+
AchievementType::OnlySolution,
320+
Some(challenge_id),
321+
Some(&solution.language),
322+
)
323+
.await?;
324+
}
315325

316-
if top_solution
317-
.as_ref()
318-
.is_none_or(|e| e.author_id == solution.author)
319-
{
320-
award_achievement(
321-
pool,
322-
solution.author,
323-
AchievementType::UncontestedFirstPlace,
324-
Some(challenge_id),
325-
Some(&solution.language),
326-
)
327-
.await?;
326+
if top_solution
327+
.as_ref()
328+
.is_none_or(|e| e.points <= solution.points)
329+
{
330+
award_achievement(
331+
pool,
332+
solution.author,
333+
AchievementType::FirstPlace,
334+
Some(challenge_id),
335+
Some(&solution.language),
336+
)
337+
.await?;
338+
}
339+
340+
if top_solution
341+
.as_ref()
342+
.is_none_or(|e| e.author_id == solution.author)
343+
{
344+
award_achievement(
345+
pool,
346+
solution.author,
347+
AchievementType::UncontestedFirstPlace,
348+
Some(challenge_id),
349+
Some(&solution.language),
350+
)
351+
.await?;
352+
}
328353
}
329354

330355
Ok(())
@@ -363,7 +388,21 @@ async fn post_updated_score(pool: &PgPool, challenge_id: i32, solution_id: i32,
363388
};
364389

365390
match challenge.challenge.challenge.status {
366-
ChallengeStatus::Beta | ChallengeStatus::Draft | ChallengeStatus::Private => return,
391+
ChallengeStatus::Beta => {
392+
if let Err(e) = award_achievement(
393+
pool,
394+
solution.author,
395+
AchievementType::SolveBeta,
396+
Some(challenge_id),
397+
None,
398+
)
399+
.await
400+
{
401+
eprintln!("Can not award achievement: {e:?}");
402+
}
403+
return;
404+
}
405+
ChallengeStatus::Draft | ChallengeStatus::Private => return,
367406
_ => (),
368407
}
369408

main-server/src/models/solutions.rs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,7 @@ pub struct LeaderboardEntry {
121121
pub author_name: String,
122122
pub author_avatar: String,
123123
pub points: i32,
124+
pub is_post_mortem: bool,
124125
}
125126

126127
#[derive(Serialize, Deserialize, Clone, Copy)]
@@ -250,7 +251,8 @@ impl LeaderboardEntry {
250251
accounts.username as author_name,
251252
accounts.avatar as author_avatar,
252253
1 as "rank!",
253-
points
254+
points,
255+
solutions.is_post_mortem
254256
FROM solutions
255257
LEFT JOIN accounts ON solutions.author = accounts.id
256258
WHERE solutions.challenge=$1 AND solutions.language=$2 AND valid=true
@@ -278,7 +280,8 @@ impl LeaderboardEntry {
278280
accounts.username as author_name,
279281
accounts.avatar as author_avatar,
280282
points,
281-
rank() OVER (ORDER BY solutions.points ASC) as "rank!"
283+
rank() OVER (ORDER BY solutions.points ASC) as "rank!",
284+
solutions.is_post_mortem
282285
FROM solutions
283286
LEFT JOIN accounts ON solutions.author = accounts.id
284287
WHERE solutions.challenge=$1 AND solutions.language=$2 AND valid=true

0 commit comments

Comments
 (0)