Skip to content

Commit

Permalink
fix: Make algorithm score setting consistent with presence of model s…
Browse files Browse the repository at this point in the history
…olutions (#1585)

* fix: Set Python scores to 10

* Lockfile

* Make algo scores consistent with model solutions

* Remove prints

* Feedback again

* Small fixes

* Iterator
  • Loading branch information
faucomte97 authored Feb 28, 2024
1 parent d3c688e commit bc244c3
Show file tree
Hide file tree
Showing 7 changed files with 452 additions and 263 deletions.
339 changes: 176 additions & 163 deletions Pipfile.lock

Large diffs are not rendered by default.

34 changes: 31 additions & 3 deletions game/admin.py
Original file line number Diff line number Diff line change
@@ -1,26 +1,54 @@
from django.contrib import admin

from game.models import Level, Block, Episode, Workspace, LevelDecor
from game.models import Level, Block, Episode, Workspace, LevelDecor, Attempt


class LevelAdmin(admin.ModelAdmin):
search_fields = ["name", "id", "owner__user__username", "owner__user__first_name"]
search_fields = [
"name",
"id",
"owner__user__username",
"owner__user__first_name",
]
raw_id_fields = ["next_level", "locked_for_class"]
readonly_fields = ["owner"]
list_display = ["name", "id", "episode", "owner"]


class EpisodeAdmin(admin.ModelAdmin):
list_display = ["id", "name"]


class WorkspaceAdmin(admin.ModelAdmin):
raw_id_fields = ["owner"]


class AttemptAdmin(admin.ModelAdmin):
search_fields = [
"level",
"student",
"start_time",
"finish_time",
"is_best_attempt",
]
raw_id_fields = ["student"]
list_display = [
"level",
"student",
"start_time",
"finish_time",
"is_best_attempt",
]


class LevelDecorAdmin(admin.ModelAdmin):
search_fields = ["level__name"]
list_display = ["id", "level", "x", "y", "decorName"]


admin.site.register(Level, LevelAdmin)
admin.site.register(Episode, EpisodeAdmin)
admin.site.register(Workspace, WorkspaceAdmin)
admin.site.register(Episode)
admin.site.register(Block)
admin.site.register(Attempt, AttemptAdmin)
admin.site.register(LevelDecor, LevelDecorAdmin)
26 changes: 13 additions & 13 deletions game/end_to_end_tests/test_play_through.py
Original file line number Diff line number Diff line change
Expand Up @@ -278,43 +278,43 @@ def test_level_079(self):
self._complete_level(79)

def test_episode_10(self):
self._complete_episode(10, 80, check_algorithm_score=False)
self._complete_episode(10, 80)

def test_level_080(self):
self._complete_level(80, check_algorithm_score=False)
self._complete_level(80)

def test_level_081(self):
self._complete_level(81, check_algorithm_score=False)
self._complete_level(81)

def test_level_082(self):
self._complete_level(82, check_algorithm_score=False)
self._complete_level(82)

def test_level_083(self):
self._complete_level(83, check_algorithm_score=False)
self._complete_level(83)

def test_level_084(self):
self._complete_level(84, check_algorithm_score=False)
self._complete_level(84)

def test_level_085(self):
self._complete_level(85, check_algorithm_score=False)
self._complete_level(85)

def test_level_086(self):
self._complete_level(86, check_algorithm_score=False)
self._complete_level(86)

def test_level_087(self):
self._complete_level(87, check_algorithm_score=False)
self._complete_level(87)

def test_level_088(self):
self._complete_level(88, check_algorithm_score=False)
self._complete_level(88)

def test_level_089(self):
self._complete_level(89, check_algorithm_score=False)
self._complete_level(89)

def test_level_090(self):
self._complete_level(90, check_algorithm_score=False)
self._complete_level(90)

def test_level_091(self):
self._complete_level(91, check_algorithm_score=False)
self._complete_level(91)

def test_episode_11(self):
self._complete_episode(11, 92, check_algorithm_score=False)
Expand Down
122 changes: 122 additions & 0 deletions game/migrations/0090_add_missing_model_solutions.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
import re

from django.apps.registry import Apps
from django.db import migrations


def add_missing_model_solutions(apps: Apps, *args):
Level = apps.get_model("game", "Level")

model_solutions = {
"80": "[5]",
"81": "[10]",
"82": "[11]",
"83": "[5]",
"84": "[5]",
"85": "[3]",
"86": "[8]",
"87": "[8]",
"88": "[11]",
"89": "[11]",
"90": "[15]",
"91": "[15]",
}

for level_name, model_solution in model_solutions.items():
count = Level.objects.filter(name=level_name).update(
model_solution=model_solution, disable_algorithm_score=False
)
assert count == 1

Attempt = apps.get_model("game", "Attempt")

attempts = Attempt.objects.filter(
level__name__in=[
"80",
"81",
"82",
"83",
"84",
"85",
"86",
"87",
"88",
"89",
"90",
"91",
],
score=10,
).prefetch_related("level")

for attempt in attempts.iterator(chunk_size=500):
workspace = attempt.workspace

# Get number of blocks from solution - 1 to discount Start block
number_of_blocks = len(re.findall("<block", workspace)) - 1

model_solution = model_solutions[attempt.level.name]

ideal_number_of_blocks = int(
model_solution.replace("[", "").replace("]", "")
)

if number_of_blocks == ideal_number_of_blocks:
attempt.score = 20
else:
difference = abs(number_of_blocks - ideal_number_of_blocks)

if difference < 10:
attempt.score += 10 - difference

attempt.save()


def remove_new_model_solutions(apps: Apps, *args):
Level = apps.get_model("game", "Level")

Level.objects.filter(
name__in=[
"80",
"81",
"82",
"83",
"84",
"85",
"86",
"87",
"88",
"89",
"90",
"91",
]
).update(model_solution="[]", disable_algorithm_score=True)

Attempt = apps.get_model("game", "Attempt")

Attempt.objects.filter(
level__name__in=[
"80",
"81",
"82",
"83",
"84",
"85",
"86",
"87",
"88",
"89",
"90",
"91",
],
score__gt=10,
).update(score=10)


class Migration(migrations.Migration):
dependencies = [("game", "0089_episodes_in_development")]
operations = [
migrations.RunPython(
add_missing_model_solutions,
reverse_code=remove_new_model_solutions,
)
]
40 changes: 40 additions & 0 deletions game/migrations/0091_disable_algo_score_if_no_model_solution.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
from django.apps.registry import Apps
from django.db import migrations


def disable_algo_score_for_levels_without_model_solution(apps: Apps, *args):
Level = apps.get_model("game", "Level")

Level.objects.filter(default=True, model_solution="[]").update(
disable_algorithm_score=True
)

Attempt = apps.get_model("game", "Attempt")

Attempt.objects.filter(
level__default=True, level__model_solution="[]", score=20
).update(score=10)


def enable_algo_score_for_levels_without_model_solution(apps: Apps, *args):
Level = apps.get_model("game", "Level")

Level.objects.filter(default=True, model_solution="[]").update(
disable_algorithm_score=False
)

Attempt = apps.get_model("game", "Attempt")

Attempt.objects.filter(
level__default=True, level__model_solution="[]", score=10
).update(score=20)


class Migration(migrations.Migration):
dependencies = [("game", "0090_add_missing_model_solutions")]
operations = [
migrations.RunPython(
disable_algo_score_for_levels_without_model_solution,
reverse_code=enable_algo_score_for_levels_without_model_solution,
)
]
20 changes: 11 additions & 9 deletions game/static/game/js/game.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,11 @@ ocargo.Game.prototype.setup = function () {
}
}


// Function being called when there is a change in game level.
ocargo.Game.prototype.onLevelChange = function() {
const currentLevelId = LEVEL_ID;
localStorage.setItem('currentEpisode', EPISODE);
const currentLevelId = LEVEL_ID;
localStorage.setItem('currentEpisode', EPISODE);
}

restoreCmsLogin()
Expand Down Expand Up @@ -123,7 +123,7 @@ ocargo.Game.prototype.onLevelChange = function() {
}

this.drawing.enablePanning()

const showMascot = BLOCKLY_ENABLED && !PYTHON_VIEW_ENABLED && LEVEL_NAME <= 80; // show mascot on Blockly-only levels that are not above 80

ocargo.Drawing.startPopup(
Expand All @@ -146,7 +146,7 @@ document.addEventListener("DOMContentLoaded", function() {
if (currentEpisode) {
localStorage.setItem('currentEpisode', currentEpisode);
}
});
});

ocargo.Game.prototype.clearWorkspaceNameInputInSaveTab = function () {
$('#workspaceNameInput').val('')
Expand Down Expand Up @@ -238,10 +238,12 @@ ocargo.Game.prototype.sendAttempt = function (score) {
return /^(GET|HEAD|OPTIONS|TRACE)$/.test(method)
}

// hack scores so that it works for demo and python TODO implement max scores and remove this!!
if (PYTHON_ENABLED) {
score *= 2
}
// TODO: Remove below - keeping it for now as a reminder that we need to
// implement some kind of algorithm score for Python levels.
// // hack scores so that it works for demo and python TODO implement max scores and remove this!!
// if (PYTHON_ENABLED) {
// score *= 2
// }

// Check that we should actually be sending an attempt - either if only blockly is enabled
// or if python is enabled and we're on the python tab (assumes they don't change tab quickly...)
Expand Down
Loading

0 comments on commit bc244c3

Please sign in to comment.