feat: implement wordle backend
This commit is contained in:
parent
04e2aed4af
commit
16b2396190
@ -0,0 +1,78 @@
|
||||
package online.wesal.wesal.controller;
|
||||
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.validation.Valid;
|
||||
import online.wesal.wesal.dto.ApiResponse;
|
||||
import online.wesal.wesal.dto.DailyChallengeResponse;
|
||||
import online.wesal.wesal.dto.LeaderboardEntry;
|
||||
import online.wesal.wesal.dto.PuzzleAttemptRequest;
|
||||
import online.wesal.wesal.entity.Puzzle;
|
||||
import online.wesal.wesal.entity.PuzzleAttempt;
|
||||
import online.wesal.wesal.service.PuzzleService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/puzzles")
|
||||
@CrossOrigin(origins = "*")
|
||||
@Tag(name = "Puzzles", description = "Wordle puzzle endpoints")
|
||||
public class PuzzleController {
|
||||
|
||||
@Autowired
|
||||
private PuzzleService puzzleService;
|
||||
|
||||
@GetMapping("/dailyChallenge")
|
||||
@Operation(summary = "Get daily challenge", description = "Returns today's Wordle word and whether user has solved it")
|
||||
public ResponseEntity<ApiResponse<DailyChallengeResponse>> getDailyChallenge() {
|
||||
try {
|
||||
Puzzle todaysPuzzle = puzzleService.getTodaysPuzzle();
|
||||
boolean solved = puzzleService.hasUserSolvedToday();
|
||||
|
||||
DailyChallengeResponse response = new DailyChallengeResponse(todaysPuzzle.getWord(), solved);
|
||||
return ResponseEntity.ok(ApiResponse.success(response));
|
||||
} catch (RuntimeException e) {
|
||||
return ResponseEntity.ok(ApiResponse.error(e.getMessage()));
|
||||
}
|
||||
}
|
||||
|
||||
@PostMapping("/attempt")
|
||||
@Operation(summary = "Submit puzzle attempt", description = "Submit user's attempt for today's puzzle")
|
||||
public ResponseEntity<ApiResponse<String>> submitAttempt(@Valid @RequestBody PuzzleAttemptRequest request) {
|
||||
try {
|
||||
puzzleService.submitAttempt(request.getAttempts(), request.getSolved());
|
||||
String message = request.getSolved() ?
|
||||
"Congratulations! You solved today's puzzle!" :
|
||||
"Better luck next time!";
|
||||
return ResponseEntity.ok(ApiResponse.success(message));
|
||||
} catch (RuntimeException e) {
|
||||
return ResponseEntity.ok(ApiResponse.error(e.getMessage()));
|
||||
}
|
||||
}
|
||||
|
||||
@GetMapping("/leaderboard")
|
||||
@Operation(summary = "Get daily leaderboard", description = "Returns leaderboard for today's puzzle ordered by submission time")
|
||||
public ResponseEntity<ApiResponse<List<LeaderboardEntry>>> getLeaderboard() {
|
||||
try {
|
||||
List<PuzzleAttempt> attempts = puzzleService.getTodaysLeaderboard();
|
||||
|
||||
List<LeaderboardEntry> leaderboard = attempts.stream()
|
||||
.map(attempt -> new LeaderboardEntry(
|
||||
attempt.getUser().getDisplayName(),
|
||||
attempt.getUser().getUsername(),
|
||||
attempt.getAttempts(),
|
||||
attempt.isSolved(),
|
||||
attempt.getSubmittedAt()
|
||||
))
|
||||
.collect(Collectors.toList());
|
||||
|
||||
return ResponseEntity.ok(ApiResponse.success(leaderboard));
|
||||
} catch (RuntimeException e) {
|
||||
return ResponseEntity.ok(ApiResponse.error(e.getMessage()));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,30 @@
|
||||
package online.wesal.wesal.dto;
|
||||
|
||||
public class DailyChallengeResponse {
|
||||
|
||||
private String wordle;
|
||||
private boolean solved;
|
||||
|
||||
public DailyChallengeResponse() {}
|
||||
|
||||
public DailyChallengeResponse(String wordle, boolean solved) {
|
||||
this.wordle = wordle;
|
||||
this.solved = solved;
|
||||
}
|
||||
|
||||
public String getWordle() {
|
||||
return wordle;
|
||||
}
|
||||
|
||||
public void setWordle(String wordle) {
|
||||
this.wordle = wordle;
|
||||
}
|
||||
|
||||
public boolean isSolved() {
|
||||
return solved;
|
||||
}
|
||||
|
||||
public void setSolved(boolean solved) {
|
||||
this.solved = solved;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,62 @@
|
||||
package online.wesal.wesal.dto;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
public class LeaderboardEntry {
|
||||
|
||||
private String displayName;
|
||||
private String username;
|
||||
private int attempts;
|
||||
private boolean solved;
|
||||
private LocalDateTime submittedAt;
|
||||
|
||||
public LeaderboardEntry() {}
|
||||
|
||||
public LeaderboardEntry(String displayName, String username, int attempts, boolean solved, LocalDateTime submittedAt) {
|
||||
this.displayName = displayName;
|
||||
this.username = username;
|
||||
this.attempts = attempts;
|
||||
this.solved = solved;
|
||||
this.submittedAt = submittedAt;
|
||||
}
|
||||
|
||||
public String getDisplayName() {
|
||||
return displayName;
|
||||
}
|
||||
|
||||
public void setDisplayName(String displayName) {
|
||||
this.displayName = displayName;
|
||||
}
|
||||
|
||||
public String getUsername() {
|
||||
return username;
|
||||
}
|
||||
|
||||
public void setUsername(String username) {
|
||||
this.username = username;
|
||||
}
|
||||
|
||||
public int getAttempts() {
|
||||
return attempts;
|
||||
}
|
||||
|
||||
public void setAttempts(int attempts) {
|
||||
this.attempts = attempts;
|
||||
}
|
||||
|
||||
public boolean isSolved() {
|
||||
return solved;
|
||||
}
|
||||
|
||||
public void setSolved(boolean solved) {
|
||||
this.solved = solved;
|
||||
}
|
||||
|
||||
public LocalDateTime getSubmittedAt() {
|
||||
return submittedAt;
|
||||
}
|
||||
|
||||
public void setSubmittedAt(LocalDateTime submittedAt) {
|
||||
this.submittedAt = submittedAt;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,39 @@
|
||||
package online.wesal.wesal.dto;
|
||||
|
||||
import jakarta.validation.constraints.Max;
|
||||
import jakarta.validation.constraints.Min;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
|
||||
public class PuzzleAttemptRequest {
|
||||
|
||||
@NotNull
|
||||
@Min(1)
|
||||
@Max(6)
|
||||
private Integer attempts;
|
||||
|
||||
@NotNull
|
||||
private Boolean solved;
|
||||
|
||||
public PuzzleAttemptRequest() {}
|
||||
|
||||
public PuzzleAttemptRequest(Integer attempts, Boolean solved) {
|
||||
this.attempts = attempts;
|
||||
this.solved = solved;
|
||||
}
|
||||
|
||||
public Integer getAttempts() {
|
||||
return attempts;
|
||||
}
|
||||
|
||||
public void setAttempts(Integer attempts) {
|
||||
this.attempts = attempts;
|
||||
}
|
||||
|
||||
public Boolean getSolved() {
|
||||
return solved;
|
||||
}
|
||||
|
||||
public void setSolved(Boolean solved) {
|
||||
this.solved = solved;
|
||||
}
|
||||
}
|
||||
50
backend/src/main/java/online/wesal/wesal/entity/Puzzle.java
Normal file
50
backend/src/main/java/online/wesal/wesal/entity/Puzzle.java
Normal file
@ -0,0 +1,50 @@
|
||||
package online.wesal.wesal.entity;
|
||||
|
||||
import jakarta.persistence.*;
|
||||
import java.time.LocalDate;
|
||||
|
||||
@Entity
|
||||
@Table(name = "puzzles")
|
||||
public class Puzzle {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Long id;
|
||||
|
||||
@Column(nullable = false, unique = true)
|
||||
private LocalDate date;
|
||||
|
||||
@Column(nullable = false, length = 5)
|
||||
private String word;
|
||||
|
||||
public Puzzle() {}
|
||||
|
||||
public Puzzle(LocalDate date, String word) {
|
||||
this.date = date;
|
||||
this.word = word;
|
||||
}
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public LocalDate getDate() {
|
||||
return date;
|
||||
}
|
||||
|
||||
public void setDate(LocalDate date) {
|
||||
this.date = date;
|
||||
}
|
||||
|
||||
public String getWord() {
|
||||
return word;
|
||||
}
|
||||
|
||||
public void setWord(String word) {
|
||||
this.word = word;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,88 @@
|
||||
package online.wesal.wesal.entity;
|
||||
|
||||
import jakarta.persistence.*;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@Entity
|
||||
@Table(name = "puzzle_attempts")
|
||||
public class PuzzleAttempt {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Long id;
|
||||
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "user_id", nullable = false)
|
||||
private User user;
|
||||
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "puzzle_id", nullable = false)
|
||||
private Puzzle puzzle;
|
||||
|
||||
@Column(nullable = false)
|
||||
private int attempts;
|
||||
|
||||
@Column(nullable = false)
|
||||
private boolean solved;
|
||||
|
||||
@Column(nullable = false)
|
||||
private LocalDateTime submittedAt;
|
||||
|
||||
public PuzzleAttempt() {}
|
||||
|
||||
public PuzzleAttempt(User user, Puzzle puzzle, int attempts, boolean solved) {
|
||||
this.user = user;
|
||||
this.puzzle = puzzle;
|
||||
this.attempts = attempts;
|
||||
this.solved = solved;
|
||||
this.submittedAt = LocalDateTime.now();
|
||||
}
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public User getUser() {
|
||||
return user;
|
||||
}
|
||||
|
||||
public void setUser(User user) {
|
||||
this.user = user;
|
||||
}
|
||||
|
||||
public Puzzle getPuzzle() {
|
||||
return puzzle;
|
||||
}
|
||||
|
||||
public void setPuzzle(Puzzle puzzle) {
|
||||
this.puzzle = puzzle;
|
||||
}
|
||||
|
||||
public int getAttempts() {
|
||||
return attempts;
|
||||
}
|
||||
|
||||
public void setAttempts(int attempts) {
|
||||
this.attempts = attempts;
|
||||
}
|
||||
|
||||
public boolean isSolved() {
|
||||
return solved;
|
||||
}
|
||||
|
||||
public void setSolved(boolean solved) {
|
||||
this.solved = solved;
|
||||
}
|
||||
|
||||
public LocalDateTime getSubmittedAt() {
|
||||
return submittedAt;
|
||||
}
|
||||
|
||||
public void setSubmittedAt(LocalDateTime submittedAt) {
|
||||
this.submittedAt = submittedAt;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,20 @@
|
||||
package online.wesal.wesal.repository;
|
||||
|
||||
import online.wesal.wesal.entity.Puzzle;
|
||||
import online.wesal.wesal.entity.PuzzleAttempt;
|
||||
import online.wesal.wesal.entity.User;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.data.jpa.repository.Query;
|
||||
import org.springframework.data.repository.query.Param;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
@Repository
|
||||
public interface PuzzleAttemptRepository extends JpaRepository<PuzzleAttempt, Long> {
|
||||
Optional<PuzzleAttempt> findByUserAndPuzzle(User user, Puzzle puzzle);
|
||||
|
||||
@Query("SELECT pa FROM PuzzleAttempt pa WHERE pa.puzzle = :puzzle ORDER BY pa.submittedAt ASC")
|
||||
List<PuzzleAttempt> findByPuzzleOrderBySubmittedAtAsc(@Param("puzzle") Puzzle puzzle);
|
||||
}
|
||||
@ -0,0 +1,13 @@
|
||||
package online.wesal.wesal.repository;
|
||||
|
||||
import online.wesal.wesal.entity.Puzzle;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.util.Optional;
|
||||
|
||||
@Repository
|
||||
public interface PuzzleRepository extends JpaRepository<Puzzle, Long> {
|
||||
Optional<Puzzle> findByDate(LocalDate date);
|
||||
}
|
||||
@ -0,0 +1,133 @@
|
||||
package online.wesal.wesal.service;
|
||||
|
||||
import online.wesal.wesal.entity.Puzzle;
|
||||
import online.wesal.wesal.entity.PuzzleAttempt;
|
||||
import online.wesal.wesal.entity.User;
|
||||
import online.wesal.wesal.repository.PuzzleAttemptRepository;
|
||||
import online.wesal.wesal.repository.PuzzleRepository;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.Random;
|
||||
|
||||
@Service
|
||||
public class PuzzleService {
|
||||
|
||||
@Autowired
|
||||
private PuzzleRepository puzzleRepository;
|
||||
|
||||
@Autowired
|
||||
private PuzzleAttemptRepository puzzleAttemptRepository;
|
||||
|
||||
@Autowired
|
||||
private UserService userService;
|
||||
|
||||
private static final List<String> WORD_LIST = Arrays.asList(
|
||||
"APPLE", "BEACH", "CHAIN", "DANCE", "EAGLE", "FLAME", "GRAPE", "HOUSE", "IMAGE", "JUDGE",
|
||||
"KNIFE", "LIGHT", "MUSIC", "NIGHT", "OCEAN", "PEACE", "QUEEN", "RIVER", "STAGE", "TIGER",
|
||||
"UNITY", "VOICE", "WATER", "YOUTH", "ZEBRA", "ANGEL", "BREAD", "CHAIR", "DREAM", "EARTH",
|
||||
"FIELD", "GLASS", "HEART", "INDEX", "JUICE", "KITE", "LEMON", "MAGIC", "NOVEL", "OPERA",
|
||||
"PLANT", "QUEST", "RADIO", "SMILE", "TOWER", "ULTRA", "VILLA", "WORLD", "YOUTH", "ZEBRA",
|
||||
"ABOUT", "ABOVE", "ABUSE", "ACTOR", "ACUTE", "ADMIT", "ADOPT", "ADULT", "AFTER", "AGAIN",
|
||||
"AGENT", "AGREE", "AHEAD", "ALARM", "ALBUM", "ALERT", "ALIEN", "ALIGN", "ALIKE", "ALIVE",
|
||||
"ALLOW", "ALONE", "ALONG", "ALTER", "AMONG", "ANGER", "ANGLE", "ANGRY", "APART", "APPLE",
|
||||
"APPLY", "ARENA", "ARGUE", "ARISE", "ARRAY", "ASIDE", "ASSET", "AVOID", "AWAKE", "AWARD",
|
||||
"AWARE", "BADLY", "BAKER", "BASES", "BASIC", "BEACH", "BEGAN", "BEGIN", "BEING", "BELOW",
|
||||
"BENCH", "BILLY", "BIRTH", "BLACK", "BLAME", "BLANK", "BLIND", "BLOCK", "BLOOD", "BOARD",
|
||||
"BOOST", "BOOTH", "BOUND", "BRAIN", "BRAND", "BRASS", "BRAVE", "BREAD", "BREAK", "BREED",
|
||||
"BRIEF", "BRING", "BROAD", "BROKE", "BROWN", "BUILD", "BUILT", "BUYER", "CABLE", "CALIF",
|
||||
"CARRY", "CATCH", "CAUSE", "CHAIN", "CHAIR", "CHAOS", "CHARM", "CHART", "CHASE", "CHEAP",
|
||||
"CHECK", "CHEST", "CHIEF", "CHILD", "CHINA", "CHOSE", "CIVIL", "CLAIM", "CLASS", "CLEAN",
|
||||
"CLEAR", "CLICK", "CLIMB", "CLOCK", "CLOSE", "CLOUD", "COACH", "COAST", "COULD", "COUNT",
|
||||
"COURT", "COVER", "CRAFT", "CRASH", "CRAZY", "CREAM", "CRIME", "CROSS", "CROWD", "CROWN",
|
||||
"CRUDE", "CURVE", "CYCLE", "DAILY", "DAMAGE", "DANCE", "DATED", "DEALT", "DEATH", "DEBUG",
|
||||
"DELAY", "DEPTH", "DOING", "DOUBT", "DOZEN", "DRAFT", "DRAMA", "DRANK", "DRAWN", "DREAM",
|
||||
"DRESS", "DRILL", "DRINK", "DRIVE", "DROVE", "DYING", "EAGER", "EARLY", "EARTH", "EIGHT",
|
||||
"ELITE", "EMPTY", "ENEMY", "ENJOY", "ENTER", "ENTRY", "EQUAL", "ERROR", "EVENT", "EVERY",
|
||||
"EXACT", "EXIST", "EXTRA", "FAITH", "FALSE", "FAULT", "FIBER", "FIELD", "FIFTH", "FIFTY",
|
||||
"FIGHT", "FINAL", "FIRST", "FIXED", "FLASH", "FLEET", "FLOOR", "FLUID", "FOCUS", "FORCE",
|
||||
"FORTH", "FORTY", "FORUM", "FOUND", "FRAME", "FRANK", "FRAUD", "FRESH", "FRONT", "FRUIT",
|
||||
"FULLY", "FUNNY", "GIANT", "GIVEN", "GLASS", "GLOBE", "GOING", "GRACE", "GRADE", "GRAND",
|
||||
"GRANT", "GRASS", "GRAVE", "GREAT", "GREEN", "GROSS", "GROUP", "GROWN", "GUARD", "GUESS",
|
||||
"GUEST", "GUIDE", "HAPPY", "HARRY", "HEART", "HEAVY", "HENCE", "HENRY", "HORSE", "HOTEL",
|
||||
"HOUSE", "HUMAN", "IDEAL", "IMAGE", "INDEX", "INNER", "INPUT", "ISSUE", "JAPAN", "JIMMY",
|
||||
"JOINT", "JONES", "JUDGE", "KNOWN", "LABEL", "LARGE", "LASER", "LATER", "LAUGH", "LAYER",
|
||||
"LEARN", "LEASE", "LEAST", "LEAVE", "LEGAL", "LEVEL", "LEWIS", "LIGHT", "LIMIT", "LINKS",
|
||||
"LIVES", "LOCAL", "LOOSE", "LOWER", "LUCKY", "LUNCH", "LYING", "MAGIC", "MAJOR", "MAKER",
|
||||
"MARCH", "MARIA", "MATCH", "MAYBE", "MAYOR", "MEANT", "MEDIA", "METAL", "MIGHT", "MINOR",
|
||||
"MINUS", "MIXED", "MODEL", "MONEY", "MONTH", "MORAL", "MOTOR", "MOUNT", "MOUSE", "MOUTH",
|
||||
"MOVED", "MOVIE", "MUSIC", "NEEDS", "NEVER", "NEWLY", "NIGHT", "NOISE", "NORTH", "NOTED",
|
||||
"NOVEL", "NURSE", "OCCUR", "OCEAN", "OFFER", "OFTEN", "ORDER", "OTHER", "OUGHT", "PAINT",
|
||||
"PANEL", "PAPER", "PARTY", "PEACE", "PETER", "PHASE", "PHONE", "PHOTO", "PIANO", "PICKED",
|
||||
"PIECE", "PILOT", "PITCH", "PLACE", "PLAIN", "PLANE", "PLANT", "PLATE", "POINT", "POUND",
|
||||
"POWER", "PRESS", "PRICE", "PRIDE", "PRIME", "PRINT", "PRIOR", "PRIZE", "PROOF", "PROUD",
|
||||
"PROVE", "QUEEN", "QUICK", "QUIET", "QUITE", "RADIO", "RAISE", "RANGE", "RAPID", "RATIO",
|
||||
"REACH", "READY", "REALM", "REBEL", "REFER", "RELAX", "RELAY", "RIDER", "RIDGE", "RIGHT",
|
||||
"RIGID", "RIVAL", "RIVER", "ROBIN", "ROGER", "ROMAN", "ROUGH", "ROUND", "ROUTE", "ROYAL",
|
||||
"RURAL", "SCALE", "SCENE", "SCOPE", "SCORE", "SENSE", "SERVE", "SEVEN", "SHALL", "SHAPE",
|
||||
"SHARE", "SHARP", "SHEET", "SHELF", "SHELL", "SHIFT", "SHINE", "SHIRT", "SHOCK", "SHOOT",
|
||||
"SHORT", "SHOWN", "SIGHT", "SILLY", "SINCE", "SIXTH", "SIXTY", "SIZED", "SKILL", "SLEEP",
|
||||
"SLIDE", "SMALL", "SMART", "SMILE", "SMITH", "SMOKE", "SNAKE", "SNOW", "SOBER", "SORRY",
|
||||
"SOUND", "SOUTH", "SPACE", "SPARE", "SPEAK", "SPEED", "SPEND", "SPENT", "SPLIT", "SPOKE",
|
||||
"SPORT", "STAFF", "STAGE", "STAKE", "STAND", "START", "STATE", "STEAM", "STEEL", "STEEP",
|
||||
"STEER", "STERN", "STICK", "STILL", "STOCK", "STONE", "STOOD", "STORE", "STORM", "STORY",
|
||||
"STRIP", "STUCK", "STUDY", "STUFF", "STYLE", "SUGAR", "SUITE", "SUPER", "SWEET", "TABLE",
|
||||
"TAKEN", "TASTE", "TAXES", "TEACH", "TEAM", "TEETH", "TERRY", "THANK", "THEFT", "THEIR",
|
||||
"THEME", "THERE", "THESE", "THICK", "THING", "THINK", "THIRD", "THOSE", "THREE", "THREW",
|
||||
"THROW", "THUMB", "TIGER", "TIGHT", "TIMES", "TIRED", "TITLE", "TODAY", "TOPIC", "TOTAL",
|
||||
"TOUCH", "TOUGH", "TOWER", "TRACK", "TRADE", "TRAIN", "TREAT", "TREND", "TRIAL", "TRIBE",
|
||||
"TRICK", "TRIED", "TRIES", "TRIP", "TRUCK", "TRULY", "TRUNK", "TRUST", "TRUTH", "TWICE",
|
||||
"UNDER", "UNDUE", "UNION", "UNITY", "UNTIL", "UPPER", "UPSET", "URBAN", "USAGE", "USUAL",
|
||||
"VALUE", "VIDEO", "VIRUS", "VISIT", "VITAL", "VOCAL", "VOICE", "WASTE", "WATCH", "WATER",
|
||||
"WHEEL", "WHERE", "WHICH", "WHILE", "WHITE", "WHOLE", "WHOSE", "WOMAN", "WOMEN", "WORLD",
|
||||
"WORRY", "WORSE", "WORST", "WORTH", "WOULD", "WRITE", "WRONG", "WROTE", "YOUNG", "YOUTH"
|
||||
);
|
||||
|
||||
public Puzzle getTodaysPuzzle() {
|
||||
LocalDate today = LocalDate.now();
|
||||
Optional<Puzzle> existingPuzzle = puzzleRepository.findByDate(today);
|
||||
|
||||
if (existingPuzzle.isPresent()) {
|
||||
return existingPuzzle.get();
|
||||
}
|
||||
|
||||
String word = generateWordForDate(today);
|
||||
Puzzle puzzle = new Puzzle(today, word);
|
||||
return puzzleRepository.save(puzzle);
|
||||
}
|
||||
|
||||
private String generateWordForDate(LocalDate date) {
|
||||
Random random = new Random(date.toEpochDay());
|
||||
return WORD_LIST.get(random.nextInt(WORD_LIST.size()));
|
||||
}
|
||||
|
||||
public boolean hasUserSolvedToday() {
|
||||
User currentUser = userService.getCurrentUser();
|
||||
Puzzle todaysPuzzle = getTodaysPuzzle();
|
||||
|
||||
Optional<PuzzleAttempt> attempt = puzzleAttemptRepository.findByUserAndPuzzle(currentUser, todaysPuzzle);
|
||||
return attempt.isPresent();
|
||||
}
|
||||
|
||||
public PuzzleAttempt submitAttempt(int attempts, boolean solved) {
|
||||
User currentUser = userService.getCurrentUser();
|
||||
Puzzle todaysPuzzle = getTodaysPuzzle();
|
||||
|
||||
Optional<PuzzleAttempt> existingAttempt = puzzleAttemptRepository.findByUserAndPuzzle(currentUser, todaysPuzzle);
|
||||
if (existingAttempt.isPresent()) {
|
||||
throw new RuntimeException("You've already attempted the daily challenge");
|
||||
}
|
||||
|
||||
PuzzleAttempt attempt = new PuzzleAttempt(currentUser, todaysPuzzle, attempts, solved);
|
||||
return puzzleAttemptRepository.save(attempt);
|
||||
}
|
||||
|
||||
public List<PuzzleAttempt> getTodaysLeaderboard() {
|
||||
Puzzle todaysPuzzle = getTodaysPuzzle();
|
||||
return puzzleAttemptRepository.findByPuzzleOrderBySubmittedAtAsc(todaysPuzzle);
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user