Skip to content

Commit

Permalink
Merge pull request #22 from kulmann/multi-matches-per-round
Browse files Browse the repository at this point in the history
Multi matches per round
  • Loading branch information
kulmann authored Nov 8, 2021
2 parents 728c4db + d6ed345 commit fdadea0
Show file tree
Hide file tree
Showing 24 changed files with 501 additions and 174 deletions.
4 changes: 2 additions & 2 deletions amd/build/app-lazy.min.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion classes/external/admin_get_round_matches.php
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ public static function request($coursemoduleid, $roundid) {
$game = util::get_game($coursemodule);
$round = util::get_round($roundid);
util::validate_round($game, $round);
$matches = $round->get_matches();
$matches = $round->get_match_entities();

// construct result
global $PAGE;
Expand Down
5 changes: 2 additions & 3 deletions classes/external/admin_get_users.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@
use external_value;
use invalid_parameter_exception;
use mod_challenge\external\exporter\user_dto;
use mod_challenge\model\participant;
use mod_challenge\util;
use moodle_exception;
use restricted_context_exception;
Expand Down Expand Up @@ -80,11 +79,11 @@ public static function request($coursemoduleid) {
$ctx = $coursemodule->context;
$result = [];
foreach($teachers as $teacher) {
$user_dto = new user_dto(user_dto::TYPE_TEACHER, util::get_user($teacher, $game->get_id()), $ctx);
$user_dto = new user_dto(user_dto::TYPE_TEACHER, $teacher, $ctx);
$result[] = $user_dto->export($renderer);
}
foreach($participants as $participant) {
$user_dto = new user_dto(user_dto::TYPE_PARTICIPANT, util::get_user($participant, $game->get_id()), $ctx);
$user_dto = new user_dto(user_dto::TYPE_PARTICIPANT, $participant, $ctx);
$result[] = $user_dto->export($renderer);
}
return $result;
Expand Down
2 changes: 1 addition & 1 deletion classes/external/admin_schedule_round.php
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ public static function request_returns() {
}

/**
* Start a new round.
* Start a new round and generate the first matches.
*
* @param int $coursemoduleid
* @param int $roundid
Expand Down
4 changes: 4 additions & 0 deletions classes/external/exporter/match_dto.php
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,10 @@ protected static function define_other_properties() {
'type' => PARAM_INT,
'description' => 'id of the game round this match takes place in',
],
'number' => [
'type' => PARAM_INT,
'description' => 'the number of this match within its round',
],
'completed' => [
'type' => PARAM_BOOL,
'description' => 'whether the match is completed (finished by both participants or ended)',
Expand Down
24 changes: 23 additions & 1 deletion classes/external/exporter/round_dto.php
Original file line number Diff line number Diff line change
Expand Up @@ -88,9 +88,25 @@ protected static function define_other_properties() {
'type' => PARAM_NOTAGS,
'description' => 'the round name',
],
'matches' => [
'type' => PARAM_INT,
'description' => 'the max. number of matches within this round',
],
'matches_created' => [
'type' => PARAM_INT,
'description' => 'the number of matches already created within this round',
],
'questions' => [
'type' => PARAM_INT,
'description' => 'the number of questions for this round. when round is started, this gets applied from the current value from the game.'
],
'started' => [
'type' => PARAM_BOOL,
'description' => 'whether or not the round has started',
],
'ended' => [
'type' => PARAM_BOOL,
'description' => 'whether or not the round has ended',
]
];
}
Expand All @@ -102,6 +118,12 @@ protected static function define_related() {
}

protected function get_other_values(renderer_base $output) {
return $this->round->to_array();
return \array_merge(
$this->round->to_array(),
[
'started' => $this->round->is_started(),
'ended' => $this->round->is_ended(),
],
);
}
}
5 changes: 2 additions & 3 deletions classes/external/main_get_mdl_users.php
Original file line number Diff line number Diff line change
Expand Up @@ -86,10 +86,9 @@ public static function request($coursemoduleid) {
$game = util::get_game($coursemodule);

// get the users and transform to output
$mdl_users = $game->get_mdl_participants(false);
$participants = $game->get_mdl_participants(false);
$result = [];
foreach($mdl_users as $mdl_user) {
$participant = util::get_user($mdl_user, $game->get_id());
foreach($participants as $participant) {
$exporter = new mdl_user_dto($participant, $ctx);
$result[] = $exporter->export($renderer);
}
Expand Down
79 changes: 58 additions & 21 deletions classes/model/game.php
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,10 @@ class game extends abstract_model {
* @var int The value for duration of a round.
*/
protected $round_duration_value;
/**
* @var int The number of matches being generated during a round.
*/
protected $round_matches;
/**
* @var int The number of rounds until the game ends.
*/
Expand Down Expand Up @@ -132,6 +136,7 @@ function __construct() {
$this->question_shuffle_answers = true;
$this->round_duration_unit = self::MOD_CHALLENGE_ROUND_DURATION_UNIT_DAYS;
$this->round_duration_value = 7;
$this->round_matches = 1;
$this->rounds = 10;
$this->winner_mdl_user = 0;
$this->winner_score = 0;
Expand Down Expand Up @@ -160,6 +165,7 @@ public function apply($data) {
$this->question_shuffle_answers = !isset($data['question_shuffle_answers']) || $data['question_shuffle_answers'] == 1;
$this->round_duration_unit = $data['round_duration_unit'] ?? self::MOD_CHALLENGE_ROUND_DURATION_UNIT_DAYS;
$this->round_duration_value = $data['round_duration_value'] ?? 7;
$this->round_matches = $data['round_matches'] ?? 1;
$this->rounds = $data['rounds'] ?? 10;
$this->winner_mdl_user = $data['winner_mdl_user'] ?? 0;
$this->winner_score = $data['winner_score'] ?? 0;
Expand All @@ -170,19 +176,18 @@ public function apply($data) {
* Select all participants within the given course.
* Important: this excludes teachers!
*
* @return stdClass[]
* @return participant[]
* @throws coding_exception
* @throws moodle_exception
* @throws dml_exception
*/
public function get_mdl_participants(bool $only_enabled) {
$mdl_users = $this->get_mdl_users_and_teachers(true, false);
$participants = $this->get_mdl_users_and_teachers(true, false);
if (!$only_enabled) {
return $mdl_users;
return $participants;
}

return \array_filter($mdl_users, function (\stdClass $mdl_user) {
$participant = util::get_user($mdl_user, $this->get_id());
return \array_filter($participants, function (participant $participant) {
return $participant->is_enabled();
});
}
Expand All @@ -191,7 +196,7 @@ public function get_mdl_participants(bool $only_enabled) {
* Select all teachers within the given course.
* Important: this excludes regular users!
*
* @return stdClass[]
* @return participant[]
* @throws coding_exception
* @throws moodle_exception
* @throws dml_exception
Expand All @@ -203,7 +208,7 @@ public function get_mdl_teachers() {
/**
* Select users within the given course.
*
* @return stdClass[]
* @return participant[]
* @throws coding_exception
* @throws moodle_exception
* @throws dml_exception
Expand All @@ -229,7 +234,7 @@ private function get_mdl_users_and_teachers(bool $include_users, bool $include_t
if ($include_teachers && !$teacher) {
continue;
}
$result[] = $user;
$result[] = util::get_user($user, $this->get_id());
}
return $result;
}
Expand Down Expand Up @@ -298,6 +303,14 @@ public function validate_round(round $round) {
}
}

// check if matches need to be generated
if ($round->are_next_matches_needed()) {
try {
$this->create_next_matches($round);
} catch (moodle_exception $ignored) {
}
}

// check if round needs to be ended
if ($round->get_state() === round::STATE_ACTIVE && $round->is_ended()) {
try {
Expand All @@ -313,7 +326,6 @@ public function validate_round(round $round) {
* - set start and end date
* - set state to active
* - select participants
* - create matches
*
* @param round $round
*
Expand All @@ -327,25 +339,43 @@ private function start_round(round $round) {
$round->set_timeend(time() + $this->calculate_round_duration_seconds());
$round->set_state(round::STATE_ACTIVE);
$round->set_questions($this->get_question_count());
$round->set_matches($this->get_round_matches());
$round->save();
return $round;
}

/**
* Creates a set of matches for the given round.
*
* @param round $round
* @throws coding_exception
* @throws dml_exception
* @throws moodle_exception
*/
private function create_next_matches(round $round) {
// get participants
$mdl_users = $this->get_mdl_participants(true);
\shuffle($mdl_users);
$participants = $this->get_mdl_participants(true);
$participants = \array_filter($participants, function (participant $participant) use ($round) {
return !$participant->has_unfinished_match($round->get_id());
});
\shuffle($participants);

// create matches
$matches = [];
while (count($mdl_users) > 1) {
$mdl_user_1 = array_shift($mdl_users);
$mdl_user_2 = array_shift($mdl_users);
// create a set of matches
$match_number = $round->get_next_match_number();
while (count($participants) > 1) {
$mdl_user_1 = array_shift($participants);
$mdl_user_2 = array_shift($participants);
$match = new match();
$match->set_mdl_user_1($mdl_user_1->id);
$match->set_mdl_user_2($mdl_user_2->id);
$match->set_mdl_user_1($mdl_user_1->get_mdl_user());
$match->set_mdl_user_2($mdl_user_2->get_mdl_user());
$match->set_round($round->get_id());
$match->set_number($match_number);
$match->save();
$matches[] = $match;
}
return $round;

// track number of created matches in round
$round->set_matches_created($match_number);
$round->save();
}

/**
Expand Down Expand Up @@ -448,7 +478,7 @@ public function stop_round(round $round, bool $force = false) {
$round->save();

// close matches if necessary
foreach ($round->get_matches() as $match) {
foreach ($round->get_match_entities() as $match) {
try {
$match->check_winner($this, $round);
} catch (moodle_exception $ignored) {
Expand Down Expand Up @@ -636,6 +666,13 @@ private function determine_round_duration_factor(): int {
}
}

/**
* @return int
*/
public function get_round_matches(): int {
return $this->round_matches;
}

/**
* @return int
*/
Expand Down
22 changes: 21 additions & 1 deletion classes/model/match.php
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,10 @@ class match extends abstract_model {
* @var int The id of the round instance this match belongs to.
*/
protected $round;
/**
* @var int The number of this match within its round (1-based).
*/
protected $number;
/**
* @var bool Whether or not the match is completed (either ended or answered by both participants).
*/
Expand Down Expand Up @@ -86,6 +90,7 @@ function __construct() {
$this->timecreated = \time();
$this->timemodified = \time();
$this->round = 0;
$this->number = 1;
$this->completed = false;
$this->mdl_user_1 = 0;
$this->mdl_user_1_notified = false;
Expand All @@ -108,10 +113,11 @@ public function apply($data) {
if (\is_object($data)) {
$data = get_object_vars($data);
}
$this->id = isset($data['id']) ? $data['id'] : 0;
$this->id = $data['id'] ?? 0;
$this->timecreated = $data['timecreated'] ?? \time();
$this->timemodified = $data['timemodified'] ?? \time();
$this->round = $data['round'];
$this->number = $data['number'] ?? 1;
$this->completed = isset($data['completed']) && intval($data['completed']) === 1;
$this->mdl_user_1 = $data['mdl_user_1'];
$this->mdl_user_1_notified = isset($data['mdl_user_1_notified']) && intval($data['mdl_user_1_notified']) === 1;
Expand Down Expand Up @@ -396,6 +402,20 @@ public function set_round(int $round) {
$this->round = $round;
}

/**
* @return int
*/
public function get_number(): int {
return $this->number;
}

/**
* @param int $number
*/
public function set_number(int $number) {
$this->number = $number;
}

/**
* @return bool
*/
Expand Down
19 changes: 18 additions & 1 deletion classes/model/participant.php
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,6 @@ public function get_full_name() {
/**
* Gets the ids of rounds that this user attended.
*
*
* @return array
* @throws \dml_exception
*/
Expand All @@ -138,6 +137,24 @@ public function get_attended_round_ids() {
return $DB->get_fieldset_sql($sql, $params);
}

/**
* Checks whether this user has an unfinished match within the given round.
*
* @param int $round_id
*
* @return bool
* @throws \dml_exception
*/
public function has_unfinished_match($round_id) {
global $DB;
$sql = "SELECT id
FROM {challenge_matches} m
WHERE m.round = :round AND ((m.mdl_user_1 = :user1 AND m.mdl_user_1_completed = 0) OR (m.mdl_user_2 = :user2 AND m.mdl_user_2_completed = 0))";
$params = ['round' => $round_id, 'user1' => $this->get_mdl_user(), 'user2' => $this->get_mdl_user()];
$match_ids = $DB->get_fieldset_sql($sql, $params);
return \count($match_ids) > 0;
}

/**
* Generates the picture url of the user, including fallback url if there is none.
*
Expand Down
Loading

0 comments on commit fdadea0

Please sign in to comment.