use of com.faendir.zachtronics.bot.sc.model.ScCategory in project zachtronics-leaderboard-bot by F43nd1r.
the class ScRecordDTO method fromCategoryRecord.
@NotNull
public static ScRecordDTO fromCategoryRecord(@NotNull CategoryRecord<ScRecord, ScCategory> categoryRecord) {
ScRecord record = categoryRecord.getRecord();
Set<ScCategory> categories = categoryRecord.getCategories();
return new ScRecordDTO(ScScoreDTO.fromScore(record.getScore()), record.getScore().toDisplayString(DisplayContext.plainText()), record.getAuthor(), record.getDisplayLink(), record.getDataLink(), UtilsKt.smartFormat(categories, UtilsKt.toMetricsTree(record.getPuzzle().getSupportedCategories())));
}
use of com.faendir.zachtronics.bot.sc.model.ScCategory in project zachtronics-leaderboard-bot by F43nd1r.
the class ScSolution method unmarshal.
@NotNull
public static ScSolution unmarshal(@NotNull String[] fields) {
assert fields.length == 5;
ScScore score = Objects.requireNonNull(ScScore.parseScore(fields[0]));
String author = fields[1];
String displayLink = fields[2];
boolean videoOnly = fields[3] != null;
String categories = fields[4];
ScSolution solution = new ScSolution(score, author, displayLink, videoOnly);
if (categories != null)
Pattern.compile(",").splitAsStream(categories).map(ScCategory::valueOf).forEach(solution.categories::add);
return solution;
}
use of com.faendir.zachtronics.bot.sc.model.ScCategory in project zachtronics-leaderboard-bot by F43nd1r.
the class ScSolutionRepository method archiveOne.
/**
* @param solutions the list is modified with the updated state
*/
@Override
@NotNull
protected SubmitResult<ScRecord, ScCategory> archiveOne(@NotNull GitRepository.ReadWriteAccess access, @NotNull List<ScSolution> solutions, @NotNull ScSubmission submission) {
ScPuzzle puzzle = submission.getPuzzle();
Path puzzlePath = getPuzzlePath(access, puzzle);
List<CategoryRecord<ScRecord, ScCategory>> beatenCategoryRecords = new ArrayList<>();
ScSolution candidate = new ScSolution(submission.getScore(), submission.getAuthor(), submission.getDisplayLink(), false);
try {
for (ListIterator<ScSolution> it = solutions.listIterator(); it.hasNext(); ) {
ScSolution solution = it.next();
int r = dominanceCompare(candidate.getScore(), solution.getScore());
if (r > 0) {
// TODO actually return all of the beating sols
CategoryRecord<ScRecord, ScCategory> categoryRecord = solution.extendToCategoryRecord(puzzle, makeArchiveLink(puzzle, solution.getScore()), makeArchivePath(puzzlePath, solution.getScore()));
return new SubmitResult.NothingBeaten<>(Collections.singletonList(categoryRecord));
} else if (r < 0) {
// allow same-score changes if you bring a video or you are the original author and don't regress the video state
if (candidate.getScore().equals(solution.getScore()) && candidate.getDisplayLink() == null && !(candidate.getAuthor().equals(solution.getAuthor()) && solution.getDisplayLink() == null)) {
return new SubmitResult.AlreadyPresent<>();
}
// remove beaten score and get categories
candidate.getCategories().addAll(solution.getCategories());
if (// video-only sols have no data
!solution.isVideoOnly())
Files.delete(makeArchivePath(puzzlePath, solution.getScore()));
// the beaten record has no data anymore
beatenCategoryRecords.add(solution.extendToCategoryRecord(puzzle, null, null));
if (candidate.getDisplayLink() == null && solution.getDisplayLink() != null) {
// we beat the solution, but we can't replace the video, we keep the solution entry as a video-only
// empty categories
it.set(solution.withVideoOnly(true));
} else {
it.remove();
}
}
}
// the new record may have gained categories of records it didn't pareto-beat, do the transfers
for (ScSolution solution : solutions) {
EnumSet<ScCategory> lostCategories = EnumSet.noneOf(ScCategory.class);
for (ScCategory category : solution.getCategories()) {
if (category.supportsScore(candidate.getScore()) && category.getScoreComparator().compare(candidate.getScore(), solution.getScore()) < 0) {
lostCategories.add(category);
}
}
if (!lostCategories.isEmpty()) {
// add a CR holding the lost categories, then correct the solutions
CategoryRecord<ScRecord, ScCategory> beatenCR = new CategoryRecord<>(solution.extendToRecord(puzzle, makeArchiveLink(puzzle, solution.getScore()), makeArchivePath(puzzlePath, solution.getScore())), lostCategories);
beatenCategoryRecords.add(beatenCR);
solution.getCategories().removeAll(lostCategories);
candidate.getCategories().addAll(lostCategories);
}
}
int index = Collections.binarySearch(solutions, candidate, COMPARATOR);
if (index < 0) {
index = -index - 1;
}
solutions.add(index, candidate);
String filename = makeScoreFilename(candidate.getScore());
Path solutionPath = puzzlePath.resolve(filename);
// ensure there is one and only one newline at the end
String data = submission.getData().replaceFirst("\\s*$", "\n");
Files.writeString(solutionPath, data, StandardOpenOption.CREATE_NEW);
marshalSolutions(solutions, puzzlePath);
} catch (IOException e) {
// failures could happen after we dirtied the repo, so we call reset&clean on the puzzle dir
access.resetAndClean(puzzlePath.toFile());
return new SubmitResult.Failure<>(e.toString());
}
if (access.status().isClean()) {
// the same exact sol was already archived,
return new SubmitResult.AlreadyPresent<>();
}
String result = commit(access, submission, puzzlePath);
return new SubmitResult.Success<>(result, beatenCategoryRecords);
}
use of com.faendir.zachtronics.bot.sc.model.ScCategory in project zachtronics-leaderboard-bot by F43nd1r.
the class ScSolutionRepository method writeToRedditLeaderboard.
@Override
protected void writeToRedditLeaderboard(@NotNull ScPuzzle puzzle, Path puzzlePath, @NotNull List<ScSolution> solutions, String updateMessage) {
Map<ScCategory, ScRecord> recordMap = new EnumMap<>(ScCategory.class);
Map<ScCategory, ScRecord> videoRecordMap = new EnumMap<>(ScCategory.class);
List<ScRecord> videoRecords = solutions.stream().filter(s -> s.getDisplayLink() != null).map(// no export needed
s -> s.extendToRecord(puzzle, null, null)).toList();
for (ScSolution solution : solutions) {
ScRecord record = solution.extendToRecord(puzzle, makeArchiveLink(puzzle, solution.getScore()), makeArchivePath(puzzlePath, solution.getScore()));
for (ScCategory category : solution.getCategories()) {
recordMap.put(category, record);
if (record.getDisplayLink() == null) {
videoRecords.stream().filter(s -> category.supportsScore(s.getScore())).min(Comparator.comparing(ScRecord::getScore, category.getScoreComparator())).ifPresent(// bosses have no videos at all
videoRecord -> videoRecordMap.put(category, videoRecord));
}
}
}
String[] lines = redditService.getWikiPage(Subreddit.SPACECHEM, puzzle.getGroup().getWikiPage()).split("\\r?\\n");
Pattern puzzleRegex = Pattern.compile("^\\| \\[" + Pattern.quote(puzzle.getDisplayName()));
int rowIdx = 0;
// | [Puzzle - 1 Reactor](https://zlbb) | [(**ccc**/**r**/ss) author](https://li.nk) | ← | [(ccc/**r**/**ss**) author](https://li.nk) | ←
for (int lineIdx = 0; lineIdx < lines.length; lineIdx++) {
String line = lines[lineIdx];
if (puzzleRegex.matcher(line).find()) {
String[] prevElems = line.trim().split("\\s*\\|\\s*", -1);
int halfSize = (prevElems.length - 2) / 2;
StringBuilder row = new StringBuilder("| ");
int minReactors = Integer.MAX_VALUE;
String rowTitle = puzzle.getDisplayName();
if (rowIdx == 1) {
minReactors = recordMap.get(ScCategory.RC).getScore().getReactors();
rowTitle += " - " + minReactors + " Reactor" + (minReactors == 1 ? "" : "s");
}
row.append(Markdown.linkOrText(rowTitle, puzzle.getLink()));
for (int block = 0; block < 2; block++) {
ScCategory[] blockCategories = CATEGORIES[2 * rowIdx + block];
ScRecord[] blockRecords = Arrays.stream(blockCategories).map(recordMap::get).toArray(ScRecord[]::new);
ScRecord[] blockVideoRecords = Arrays.stream(blockCategories).map(videoRecordMap::get).toArray(ScRecord[]::new);
for (int i = 0; i < halfSize; i++) {
ScCategory thisCategory = blockCategories[i];
row.append(" | ");
if (blockRecords[i] != null) {
DisplayContext<ScCategory> displayContext = new DisplayContext<>(StringFormat.REDDIT, thisCategory);
String cell = makeLeaderboardCell(blockRecords, i, minReactors, displayContext);
row.append(cell);
if (blockVideoRecords[i] != null) {
String videoCell = makeLeaderboardCell(blockVideoRecords, i, Integer.MAX_VALUE, displayContext);
if (!cell.equals(videoCell))
row.append(". Top video ").append(videoCell);
}
} else
row.append(prevElems[2 + block * halfSize + i]);
}
}
lines[lineIdx] = row.toString();
rowIdx++;
} else if (rowIdx != 0) {
// we've already found the point and now we're past it, we're done
break;
}
}
redditService.updateWikiPage(Subreddit.SPACECHEM, puzzle.getGroup().getWikiPage(), String.join("\n", lines), updateMessage);
}
use of com.faendir.zachtronics.bot.sc.model.ScCategory in project zachtronics-leaderboard-bot by F43nd1r.
the class SolRepoFindTest method testFindCategoryHolders.
@Test
public void testFindCategoryHolders() {
ScPuzzle puzzle = ScPuzzle.research_example_1;
List<CategoryRecord<ScRecord, ScCategory>> categoryHolders = repository.findCategoryHolders(puzzle, false);
assertEquals(2, categoryHolders.size());
List<ScCategory> coveredCategories = categoryHolders.stream().map(CategoryRecord::getCategories).flatMap(Set::stream).sorted().toList();
assertEquals(puzzle.getSupportedCategories(), coveredCategories);
}
Aggregations