- LogicTask, LogicTaskBase added
- LuaUtils extended ...
This commit is contained in:
parent
0c76214bec
commit
4a2079cf10
@ -362,9 +362,18 @@ class Game extends AutoStoring
|
||||
$columns = Game::getTableColumnIndices($header); // fetch column names
|
||||
|
||||
// start iterating over tasks
|
||||
$count = 0;
|
||||
for ($i = 1; $i < $n; $i++) {
|
||||
$row = &$table[$i]; // fetch row
|
||||
|
||||
$flags = Game::explodeFlags($row[0] ?? ""); // get flags
|
||||
if (in_array("hidden", $flags)) { // skip hidden tasks
|
||||
continue;
|
||||
}
|
||||
|
||||
// count in this task
|
||||
$count++;
|
||||
|
||||
// prepare a function that looks up the fields referenced by their labels
|
||||
$select_fn = function (array $cols) use (&$row, &$columns) {
|
||||
for ($i = 0; $i < count($cols); $i++) {
|
||||
@ -382,7 +391,7 @@ class Game extends AutoStoring
|
||||
|
||||
// fetch generic fields
|
||||
$a = [
|
||||
"flags" => Game::explodeFlags($row[0] ?? ""),
|
||||
"flags" => $flags,
|
||||
"type" => strtolower($select_fn(["Típus", "Type"])),
|
||||
"image_data" => $select_fn(["Kép", "Image"]),
|
||||
"question" => $select_fn(["Kérdés", "Question"]),
|
||||
@ -403,6 +412,7 @@ class Game extends AutoStoring
|
||||
$a["instruction"] = $row[$fuc];
|
||||
break;
|
||||
case "truthtable":
|
||||
case "logicfunction":
|
||||
$a["input_variables"] = Utils::str2a($row[$fuc]);
|
||||
$a["output_variable"] = $row[$fuc + 1];
|
||||
$a["expression"] = $row[$fuc + 2];
|
||||
@ -426,9 +436,18 @@ class Game extends AutoStoring
|
||||
|
||||
// generate the task
|
||||
$this->tasks[] = TaskFactory::fromArray($a, $this);
|
||||
|
||||
// assign scoring strategy
|
||||
$sct = $select_fn(["Pontozás", "Scoring"]);
|
||||
if ($sct !== "") {
|
||||
$sct_fields = Utils::str2kv($sct);
|
||||
foreach ($sct_fields as $key => $value) {
|
||||
$sct_fields[$key] = $value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$result["n"] = $n - 1;
|
||||
$result["n"] = $count;
|
||||
return $result;
|
||||
}
|
||||
|
||||
|
||||
@ -51,11 +51,12 @@ class LogicFunction implements JsonSerializable
|
||||
// printf("%d ", $vars[$this->input_vars[$k]]);
|
||||
}
|
||||
$out = self::$EXP_LANG->evaluate($expression, $vars);
|
||||
$out_str = $out ? "1" : "0";
|
||||
// printf("%d\n", $out);
|
||||
$tt[] = $out;
|
||||
$tt[] = $out_str;
|
||||
}
|
||||
|
||||
return join("", array_map(fn($r) => ($r === True) ? 1 : 0, $tt));
|
||||
return join("", $tt);
|
||||
}
|
||||
|
||||
public static function genRandom(array $input_vars, int $min_depth = 2, int $max_depth = 3): LogicFunction
|
||||
@ -93,7 +94,7 @@ class LogicFunction implements JsonSerializable
|
||||
return new LogicFunction($term, $input_vars);
|
||||
}
|
||||
|
||||
public static function genRandomDF($input_vars): LogicFunction
|
||||
public static function genRandomDNF($input_vars): LogicFunction
|
||||
{
|
||||
$N = count($input_vars);
|
||||
$states = pow(2, $N);
|
||||
@ -101,7 +102,7 @@ class LogicFunction implements JsonSerializable
|
||||
$verilog_term = "";
|
||||
for ($i = 0; $i < $states; $i++) {
|
||||
$inside = "";
|
||||
$omit = random_int(0, 1); // omit the variable or not?
|
||||
$omit = random_int(0, 1); // omit the term or not?
|
||||
if (!$omit) {
|
||||
for ($j = 0; $j < $N; $j++) {
|
||||
$neg = !($i & (1 << $j)); // is it an inverted variable?
|
||||
|
||||
34
class/LuaUtils.php
Normal file
34
class/LuaUtils.php
Normal file
@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
require_once "LogicFunction.php";
|
||||
|
||||
class LuaUtils
|
||||
{
|
||||
public static function l2pA(array $la): array {
|
||||
$r = [];
|
||||
$i = 0;
|
||||
foreach ($la as $v) {
|
||||
$r[$i++] = $v;
|
||||
}
|
||||
return $r;
|
||||
}
|
||||
|
||||
public static function registerLuaLibs(LuaSandbox &$sandbox): void {
|
||||
self::registerLogicUtils($sandbox);
|
||||
}
|
||||
|
||||
public static function registerLogicUtils(LuaSandbox &$sandbox): void {
|
||||
$sandbox->registerLibrary("logic", [
|
||||
"extend" => fn() => [ call_user_func(array(LogicUtils::class, "extend"), ...func_get_args()) ],
|
||||
"complement" => fn() => [ call_user_func(array(LogicUtils::class, "complement"), ...func_get_args()) ],
|
||||
"changeRepresentation" => fn() => [ call_user_func(array(LogicUtils::class, "changeRepresentation"), ...func_get_args()) ],
|
||||
"genRandomLogicFunction" => fn($iv, $mind, $maxd) => [ call_user_func(array(LogicFunction::class, "genRandom"), self::l2pA($iv), $mind, $maxd)->getExpression() ],
|
||||
"genRandomLogicFunctionDNF" => fn($iv) => [ call_user_func(array(LogicFunction::class, "genRandomDNF"), self::l2pA($iv))->getExpression() ],
|
||||
"isLogicFunctionValid" => fn($expr) => [ (new LogicFunction($expr))->isValid() ],
|
||||
"isLogicFunctionADNF" => fn($iv, $expr) => [ call_user_func(array(LogicFunction::class, "isCorrectDNF"), self::l2pA($iv), $expr) ],
|
||||
"collectVariablesFromLogicFunction" => fn($expr) => call_user_func(array(LogicFunction::class, "collectVariables"), $expr),
|
||||
"convertLogicFunctionToTruthTable" => fn($expr, $iv) => [ (new LogicFunction($expr, self::l2pA($iv)))->getTruthTable() ],
|
||||
"drawLogicFunction" => fn($expr, $iv, $ov) => [ (new LogicFunction($expr, self::l2pA($iv)))->drawNetwork($ov) ],
|
||||
]);
|
||||
}
|
||||
}
|
||||
@ -37,6 +37,8 @@ class Task implements JsonSerializable
|
||||
return [ str_replace("{{" . $field . "}}", $replacement, $str) ];
|
||||
}
|
||||
]);
|
||||
|
||||
LuaUtils::registerLuaLibs($this->lua_sandbox);
|
||||
}
|
||||
private function createLuaSandbox(): void {
|
||||
if ($this->lua_sandbox === null) {
|
||||
@ -116,7 +118,7 @@ class Task implements JsonSerializable
|
||||
}
|
||||
|
||||
function setMark(float $mark): void {
|
||||
$this->mark = $mark;
|
||||
$this->mark = max($mark, 0.0);
|
||||
}
|
||||
|
||||
function getMark(): float
|
||||
@ -129,7 +131,7 @@ class Task implements JsonSerializable
|
||||
}
|
||||
|
||||
protected function staticCheck(): void {
|
||||
return;
|
||||
$this->mark = $this->max_mark;
|
||||
}
|
||||
|
||||
function autoCheck(): void {
|
||||
@ -249,7 +251,6 @@ class Task implements JsonSerializable
|
||||
if ($this->lua_script !== "") {
|
||||
$this->luaRandomize();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
function setGovernor(Game|Test|null &$governor): void {
|
||||
|
||||
@ -10,6 +10,8 @@ require_once "Tasks/TruthTableTask.php";
|
||||
|
||||
require_once "Tasks/VerilogTask.php";
|
||||
|
||||
require_once "Tasks/LogicFunctionTask.php";
|
||||
|
||||
class TaskFactory
|
||||
{
|
||||
static function fromArray(array $a, Game|Test|null &$governor = null): Task|null
|
||||
@ -28,6 +30,9 @@ class TaskFactory
|
||||
case "truthtable":
|
||||
$task = new TruthTableTask($a);
|
||||
break;
|
||||
case "logicfunction":
|
||||
$task = new LogicFunctionTask($a);
|
||||
break;
|
||||
case "verilog":
|
||||
$task = new VerilogTask($a);
|
||||
break;
|
||||
|
||||
59
class/Tasks/LogicFunctionTask.php
Normal file
59
class/Tasks/LogicFunctionTask.php
Normal file
@ -0,0 +1,59 @@
|
||||
<?php
|
||||
|
||||
require_once "LogicTaskBase.php";
|
||||
|
||||
class LogicFunctionTask extends LogicTaskBase
|
||||
{
|
||||
private float $missing_minterm_score = -1.0; // score for a missing minterm
|
||||
public function __construct(array $a = null)
|
||||
{
|
||||
parent::__construct("logicfunction", $a);
|
||||
|
||||
$this->missing_minterm_score = $a["missing_minterm_score"] ?? -1.0;
|
||||
|
||||
if (!$this->hasFlag("dnf")) {
|
||||
$this->setCorrectAnswer($this->getLogicFunction()->getExpression());
|
||||
} else {
|
||||
$this->setCorrectAnswer($this->getLogicFunction()->toDNF());
|
||||
}
|
||||
}
|
||||
|
||||
public function toArray(string $mode = "all"): array
|
||||
{
|
||||
$a = parent::toArray($mode);
|
||||
|
||||
if ($this->hasFlag("attachtruthtable")) {
|
||||
$a["truthtable"] = $this->getTruthtable();
|
||||
}
|
||||
|
||||
if ($mode == "all") {
|
||||
$a["missing_minterm_score"] = $this->missing_minterm_score;
|
||||
}
|
||||
|
||||
return $a;
|
||||
}
|
||||
|
||||
public function getMissingMintermScore(): float
|
||||
{
|
||||
return $this->missing_minterm_score;
|
||||
}
|
||||
|
||||
public function setMissingMintermScore(float $missing_minterm_score): void
|
||||
{
|
||||
$this->missing_minterm_score = $missing_minterm_score;
|
||||
}
|
||||
|
||||
protected function staticCheck(): void
|
||||
{
|
||||
$mark = 0.0;
|
||||
$pa = $this->player_answer ?? "";
|
||||
$iv = $this->getLogicFunction()->getInputVars();
|
||||
$palf = new LogicFunction($pa, $iv);
|
||||
if ($palf->isValid() && ((!$this->hasFlag("dnf")) || LogicFunction::isCorrectDNF($iv, $pa))) { // if the function is valid AND (if enabled) it is a correct DNF
|
||||
$patt = $palf->getTruthTable();
|
||||
$errs = $this->getTTDiffCntToCA($patt);
|
||||
$mark = $this->getMaxMark() + $errs * $this->getMissingMintermScore();
|
||||
}
|
||||
$this->setMark($mark);
|
||||
}
|
||||
}
|
||||
111
class/Tasks/LogicTaskBase.php
Normal file
111
class/Tasks/LogicTaskBase.php
Normal file
@ -0,0 +1,111 @@
|
||||
<?php
|
||||
|
||||
require_once "PicturedTask.php";
|
||||
|
||||
require_once "class/LogicFunction.php";
|
||||
|
||||
require_once "class/Utils.php";
|
||||
|
||||
require_once "class/LuaUtils.php";
|
||||
|
||||
class LogicTaskBase extends PicturedTask
|
||||
{
|
||||
private LogicFunction $lf; // logic function
|
||||
private string $output_variable; // output variable
|
||||
|
||||
public function __construct(string $type, array $a = null)
|
||||
{
|
||||
parent::__construct($type, $a);
|
||||
|
||||
if (isset($a["function"])) { // fetching from a JSON-stored object
|
||||
$this->lf = LogicFunction::fromArray($a["function"]);
|
||||
} else if (isset($a["expression"], $a["input_variables"])) { // building from the scratch
|
||||
$this->lf = new LogicFunction($a["expression"], $a["input_variables"]);
|
||||
} else {
|
||||
$this->lf = new LogicFunction();
|
||||
}
|
||||
|
||||
$this->output_variable = $a["output_variable"] ?? "f";
|
||||
}
|
||||
|
||||
public function setOutputVariable(string $ovar): void {
|
||||
$this->output_variable = $ovar;
|
||||
}
|
||||
|
||||
public function getOutputVariable(): string {
|
||||
return $this->output_variable;
|
||||
}
|
||||
|
||||
public function setLogicFunction(LogicFunction $lf): void {
|
||||
$this->lf = $lf;
|
||||
}
|
||||
|
||||
public function getLogicFunction(): LogicFunction {
|
||||
return $this->lf;
|
||||
}
|
||||
|
||||
protected function getTTDiffCntToCA(string $ott): int {
|
||||
$cans_tt = $this->getTruthTable();
|
||||
$errs = 0;
|
||||
for ($i = 0; $i < $this->getLogicFunction()->getNStates(); $i++) {
|
||||
if (($ott[$i] ?? " ") != $cans_tt[$i]) {
|
||||
$errs++;
|
||||
}
|
||||
}
|
||||
return $errs;
|
||||
}
|
||||
|
||||
public function toArray(string $mode = "all"): array
|
||||
{
|
||||
$a = parent::toArray($mode);
|
||||
|
||||
if ($mode === "all") {
|
||||
$a["function"] = $this->lf->toArray();
|
||||
}
|
||||
|
||||
$a["output_variable"] = $this->output_variable;
|
||||
$a["input_variables"] = $this->lf->getInputVars();
|
||||
|
||||
return $a;
|
||||
}
|
||||
|
||||
public function randomize(): void
|
||||
{
|
||||
parent::randomize();
|
||||
|
||||
if ($this->hasFlag("drawnetwork")) {
|
||||
$this->setImageData($this->lf->drawNetwork($this->output_variable));
|
||||
$this->setImageType("html");
|
||||
}
|
||||
}
|
||||
|
||||
// ---- Lua specific ----
|
||||
|
||||
public function generateRandomFunction(array $input_vars, int $min_depth, int $max_depth): void {
|
||||
$this->lf = LogicFunction::genRandom(LuaUtils::l2pA($input_vars), $min_depth, $max_depth);
|
||||
}
|
||||
|
||||
public function generateRandomDF(array $input_vars): void {
|
||||
$this->lf = LogicFunction::genRandomDNF(LuaUtils::l2pA($input_vars));
|
||||
}
|
||||
|
||||
public function getFunctionAsDNF(): string {
|
||||
return $this->lf->toDNF();
|
||||
}
|
||||
|
||||
public function setLogicFunctionExpr(string $expr, array $input_vars = []): void {
|
||||
$this->lf = new LogicFunction($expr, LuaUtils::l2pA($input_vars));
|
||||
}
|
||||
|
||||
public function getLogicFunctionExpr(string $fmt = "verilog_bitwise"): string {
|
||||
return $this->lf->getExpression($fmt);
|
||||
}
|
||||
|
||||
public function getLogicFunctionDNF(): string {
|
||||
return $this->lf->toDNF();
|
||||
}
|
||||
|
||||
public function getTruthTable(): string {
|
||||
return $this->lf->getTruthTable();
|
||||
}
|
||||
}
|
||||
@ -106,9 +106,9 @@ class NumberConversionTask extends OpenEndedTask
|
||||
{
|
||||
$mark = 0.0;
|
||||
if ($this->hasFlag("acceptwithoutleadingzeros")) {
|
||||
$mark = (ltrim($this->player_answer, " 0") === ltrim($this->correct_answer, "0")) ? 1.0 : 0.0;
|
||||
$mark = (ltrim($this->player_answer, " 0") === ltrim($this->correct_answer, "0")) ? $this->getMaxMark() : 0.0;
|
||||
} else {
|
||||
$mark = (trim($this->player_answer) === trim($this->correct_answer)) ? 1.0 : 0.0;
|
||||
$mark = (trim($this->player_answer) === trim($this->correct_answer)) ? $this->getMaxMark() : 0.0;
|
||||
}
|
||||
$this->setMark($mark);
|
||||
}
|
||||
|
||||
@ -48,7 +48,7 @@ class OpenEndedTask extends PicturedTask
|
||||
|
||||
public function staticCheck(): void
|
||||
{
|
||||
$mark = in_array($this->player_answer, $this->correct_answer) ? 1.0 : 0.0;
|
||||
$mark = in_array($this->player_answer, $this->correct_answer) ? $this->getMaxMark() : 0.0;
|
||||
$this->setMark($mark);
|
||||
}
|
||||
|
||||
|
||||
@ -32,6 +32,16 @@ class PicturedTask extends Task
|
||||
return $this->image_type;
|
||||
}
|
||||
|
||||
function setUrlData(string $url_data): void {
|
||||
$this->setImageData($url_data);
|
||||
$this->setImageType("url");
|
||||
}
|
||||
|
||||
function setHtmlData(string $svg_data): void {
|
||||
$this->setImageData($svg_data);
|
||||
$this->setImageType("html");
|
||||
}
|
||||
|
||||
function toArray(string $mode = "all"): array
|
||||
{
|
||||
$a = parent::toArray($mode);
|
||||
|
||||
@ -55,7 +55,7 @@ class SingleChoiceTask extends PicturedTask
|
||||
|
||||
function staticCheck(): void
|
||||
{
|
||||
$mark = ($this->player_answer == $this->correct_answer) ? 1.0 : 0.0;
|
||||
$mark = ($this->player_answer == $this->correct_answer) ? $this->getMaxMark() : 0.0;
|
||||
$this->setMark($mark);
|
||||
}
|
||||
|
||||
|
||||
@ -1,119 +1,42 @@
|
||||
<?php
|
||||
|
||||
require_once "OpenEndedTask.php";
|
||||
require_once "LogicTaskBase.php";
|
||||
|
||||
require_once "class/LogicFunction.php";
|
||||
|
||||
require_once "class/Utils.php";
|
||||
|
||||
class TruthTableTask extends PicturedTask
|
||||
class TruthTableTask extends LogicTaskBase
|
||||
{
|
||||
private LogicFunction $lf; // logic functions
|
||||
private string $output_variable; // output variable
|
||||
private float $bad_line_score; // points for a bad line in the truth table
|
||||
public function __construct(array $a = null)
|
||||
{
|
||||
parent::__construct("truthtable", $a);
|
||||
|
||||
if (isset($a["function"])) { // fetching from a JSON-stored object
|
||||
$this->lf = LogicFunction::fromArray($a["function"]);
|
||||
} else if (isset($a["expression"], $a["input_variables"])) { // building from the scratch
|
||||
$this->lf = new LogicFunction($a["expression"], $a["input_variables"]);
|
||||
} else {
|
||||
$this->lf = new LogicFunction();
|
||||
$this->bad_line_score = $a["bad_line_score"] ?? -1.0;
|
||||
}
|
||||
|
||||
$this->setOutputVariable($a["output_variable"] ?? "f");
|
||||
function setBadLineScore(float $bad_line_score): void {
|
||||
$this->bad_line_score = $bad_line_score;
|
||||
}
|
||||
|
||||
function getBadLineScore(): float {
|
||||
return $this->bad_line_score;
|
||||
}
|
||||
|
||||
public function staticCheck(): void
|
||||
{
|
||||
$ans_tt = $this->player_answer;
|
||||
$cans_tt = $this->lf->getTruthTable();
|
||||
$errs = 0;
|
||||
for ($i = 0; $i < $this->lf->getNStates(); $i++) {
|
||||
if (($ans_tt[$i] ?? " ") != $cans_tt[$i]) {
|
||||
$errs++;
|
||||
}
|
||||
}
|
||||
$mark = ($errs === 0) ? 1.0 : 0.0;
|
||||
$errs = $this->getTTDiffCntToCA($this->player_answer ?? "");
|
||||
$mark = $this->getMaxMark() + $errs * $this->getBadLineScore();
|
||||
$this->setMark($mark);
|
||||
}
|
||||
|
||||
public function setOutputVariable(string $ovar): void {
|
||||
$this->output_variable = $ovar;
|
||||
}
|
||||
|
||||
public function getOutputVariable(): string {
|
||||
return $this->output_variable;
|
||||
}
|
||||
|
||||
public function setLogicFunction(LogicFunction $lf): void {
|
||||
$this->lf = $lf;
|
||||
}
|
||||
|
||||
public function getLogicFunction(): LogicFunction {
|
||||
return $this->lf;
|
||||
}
|
||||
|
||||
public static function sanitizeIndices(array $a): array {
|
||||
$r = [];
|
||||
$i = 0;
|
||||
foreach ($a as $v) {
|
||||
$r[$i++] = $v;
|
||||
}
|
||||
return $r;
|
||||
}
|
||||
|
||||
public function generateRandomFunction(array $input_vars, int $min_depth, int $max_depth): void {
|
||||
$this->lf = LogicFunction::genRandom(self::sanitizeIndices($input_vars), $min_depth, $max_depth);
|
||||
}
|
||||
|
||||
public function generateRandomDF(array $input_vars): void {
|
||||
$this->lf = LogicFunction::genRandomDF(self::sanitizeIndices($input_vars));
|
||||
}
|
||||
|
||||
public function getFunctionAsDNF(): string {
|
||||
return $this->lf->toDNF();
|
||||
}
|
||||
|
||||
public function isValidDNF(): bool {
|
||||
return LogicFunction::isCorrectDNF($this->lf->getInputVars(), $this->lf->getExpression());
|
||||
}
|
||||
|
||||
public function setLogicFunctionExpr(string $expr, array $input_vars = []): void {
|
||||
$this->lf = new LogicFunction($expr, self::sanitizeIndices($input_vars));
|
||||
}
|
||||
|
||||
public function getLogicFunctionExpr(string $fmt = "verilog_bitwise"): string {
|
||||
return $this->lf->getExpression($fmt);
|
||||
}
|
||||
|
||||
public function getLogicFunctionDNF(): string {
|
||||
return $this->lf->toDNF();
|
||||
}
|
||||
|
||||
public function toArray(string $mode = "all"): array
|
||||
{
|
||||
$a = parent::toArray($mode);
|
||||
|
||||
if ($mode === "all") {
|
||||
$a["function"] = $this->lf->toArray();
|
||||
$a["bad_line_score"] = $this->bad_line_score;
|
||||
}
|
||||
|
||||
$a["correct_answer"] = $this->lf->getTruthTable();
|
||||
$a["input_variables"] = $this->lf->getInputVars();
|
||||
$a["output_variable"] = $this->output_variable;
|
||||
$a["correct_answer"] = $this->getLogicFunction()->getTruthTable();
|
||||
|
||||
return $a;
|
||||
}
|
||||
|
||||
public function randomize(): void
|
||||
{
|
||||
parent::randomize();
|
||||
|
||||
if ($this->hasFlag("drawnetwork")) {
|
||||
$this->setImageData($this->lf->drawNetwork($this->output_variable));
|
||||
$this->setImageType("svg");
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -6,6 +6,7 @@ class VerilogTask extends PicturedTask
|
||||
{
|
||||
private string $test_bench_fn; // test bench file name
|
||||
private string $compile_log; // short explanation for the marking
|
||||
private float $error_score; // points received for an error
|
||||
private const FAILED_MARK = "[FAILED]"; // mark at the beginning of testbench results indicating a failed combination
|
||||
|
||||
public function __construct(array &$a = null)
|
||||
@ -14,6 +15,7 @@ class VerilogTask extends PicturedTask
|
||||
|
||||
$this->compile_log = $a["compile_log"] ?? "";
|
||||
$this->test_bench_fn = $a["test_bench_fn"] ?? "";
|
||||
$this->error_score = $a["error_score"] ?? -1.0;
|
||||
}
|
||||
|
||||
private function verifyCode(): bool
|
||||
@ -26,7 +28,7 @@ class VerilogTask extends PicturedTask
|
||||
return true;
|
||||
}
|
||||
|
||||
private function executeTest(): bool
|
||||
private function executeTest(): int
|
||||
{
|
||||
// store the user's answer
|
||||
$module_code_fn = tempnam(sys_get_temp_dir(), "verilogtask_user_module_");
|
||||
@ -74,7 +76,17 @@ class VerilogTask extends PicturedTask
|
||||
@unlink($test_bench_fn);
|
||||
@unlink($output_fn);
|
||||
|
||||
return $failed_count == 0;
|
||||
return $failed_count;
|
||||
}
|
||||
|
||||
public function setErrorScore(float $error_score): void
|
||||
{
|
||||
$this->error_score = $error_score;
|
||||
}
|
||||
|
||||
public function getErrorScore(): float
|
||||
{
|
||||
return $this->error_score;
|
||||
}
|
||||
|
||||
public function staticCheck(): void
|
||||
@ -85,10 +97,12 @@ class VerilogTask extends PicturedTask
|
||||
$mark = 0.0;
|
||||
if ($this->verifyCode()) {
|
||||
// run the simulation
|
||||
$test_ok = $this->executeTest();
|
||||
|
||||
$mark = $test_ok ? 1.0 : 0.0; // FIXME
|
||||
$failed_count = $this->executeTest();
|
||||
if ($failed_count != PHP_INT_MAX) {
|
||||
$mark = $this->getMaxMark() + $failed_count * $this->error_score;
|
||||
}
|
||||
}
|
||||
|
||||
$this->setMark($mark);
|
||||
}
|
||||
|
||||
@ -100,6 +114,7 @@ class VerilogTask extends PicturedTask
|
||||
|
||||
if ($mode == "all") {
|
||||
$a["test_bench_fn"] = $this->test_bench_fn;
|
||||
$a["error_score"] = $this->error_score;
|
||||
}
|
||||
|
||||
return $a;
|
||||
|
||||
@ -7,7 +7,22 @@ class Utils
|
||||
public static function str2kv(string $str): array
|
||||
{
|
||||
preg_match_all("/([^,= ]+)=([^,= ]+)/", $str, $r);
|
||||
return array_combine($r[1], $r[2]);
|
||||
$a = array_combine($r[1], $r[2]);
|
||||
|
||||
foreach ($a as &$v) {
|
||||
if (is_numeric($v)) { // is it a numeric value?
|
||||
if (((int)$v) == ((double)$v)) { // is it an integer?
|
||||
$v = (int)$v;
|
||||
} else { // is it a float?
|
||||
$v = (double)$v;
|
||||
}
|
||||
} else if (in_array(strtolower($v), ["true", "false"]) ) { // it's a boolean
|
||||
$v = $v === "true";
|
||||
} else if (str_starts_with($v, '"') && str_ends_with($v, '"')) { // it's a string
|
||||
$v = substr($v, 1, strlen($v) - 2); // strip leading and trailing quotes
|
||||
}
|
||||
}
|
||||
return $a;
|
||||
}
|
||||
|
||||
public static function str2a(string $str): array {
|
||||
|
||||
174
js/tasks.js
174
js/tasks.js
@ -36,19 +36,26 @@ class Task extends HTMLElement {
|
||||
margin-bottom: 0.5em;
|
||||
border-radius: 0.3em;
|
||||
}
|
||||
section.seq-num {
|
||||
section.seq-num, section.mark {
|
||||
display: block;
|
||||
position: absolute;
|
||||
right: 0;
|
||||
padding: 0.5em;
|
||||
bottom: 0;
|
||||
background-color: #176767;
|
||||
color: whitesmoke;
|
||||
border-bottom-right-radius: 0.3em;
|
||||
border-top-left-radius: 0.3em;
|
||||
width: 2em;
|
||||
z-index: 10;
|
||||
}
|
||||
section.seq-num {
|
||||
bottom: 0;
|
||||
border-bottom-right-radius: 0.3em;
|
||||
border-top-left-radius: 0.3em;
|
||||
}
|
||||
section.mark {
|
||||
top: 0;
|
||||
border-top-right-radius: 0.3em;
|
||||
border-bottom-left-radius: 0.3em;
|
||||
}
|
||||
section.answer-container {
|
||||
/* (empty) */
|
||||
}
|
||||
@ -56,7 +63,7 @@ class Task extends HTMLElement {
|
||||
background-color: #e5d8d3;
|
||||
}
|
||||
code {
|
||||
font-family: 'Monaco', monospace;
|
||||
font-family: 'Source Code Pro', monospace;
|
||||
}
|
||||
|
||||
section#correct-answer {
|
||||
@ -96,6 +103,8 @@ class Task extends HTMLElement {
|
||||
question_span.classList.add("question");
|
||||
let answer_container = document.createElement("section");
|
||||
answer_container.classList.add("answer-container");
|
||||
let mark_section = document.createElement("section");
|
||||
mark_section.classList.add("mark");
|
||||
let seq_num_section = document.createElement("section");
|
||||
seq_num_section.classList.add("seq-num");
|
||||
seq_num_section.innerText = `${this.sequence_number + 1}.`;
|
||||
@ -106,6 +115,7 @@ class Task extends HTMLElement {
|
||||
|
||||
task_box.append(question_span);
|
||||
task_box.append(answer_container);
|
||||
task_box.append(mark_section);
|
||||
task_box.append(seq_num_section);
|
||||
task_box.append(ca_section);
|
||||
task_box.append(comment_sec);
|
||||
@ -115,6 +125,7 @@ class Task extends HTMLElement {
|
||||
this.task_box = task_box;
|
||||
this.question_span = question_span;
|
||||
this.answer_container = answer_container;
|
||||
this.mark_section = mark_section;
|
||||
this.seq_num_section = seq_num_section;
|
||||
this.ca_section = ca_section;
|
||||
this.comment_sec = comment_sec;
|
||||
@ -198,10 +209,21 @@ class Task extends HTMLElement {
|
||||
|
||||
}
|
||||
|
||||
displayMarking() {
|
||||
if (this.isConcluded) {
|
||||
this.mark_section.innerText = `${this.mark}/${this.max_mark}`;
|
||||
} else {
|
||||
this.mark_section.innerText = `${this.max_mark}`;
|
||||
}
|
||||
this.mark_section.innerHTML += `<span style="font-size: 70%">p</span>`
|
||||
}
|
||||
|
||||
fromArray(a) {
|
||||
this.setQuestion(a["question"]);
|
||||
this.playerAnswer = a["player_answer"];
|
||||
this.correctAnswer = a["correct_answer"];
|
||||
this.max_mark = a["max_mark"];
|
||||
this.mark = a["mark"];
|
||||
|
||||
let comment = a["comment"];
|
||||
if ((comment !== undefined) && (comment !== null) && (comment !== "")) {
|
||||
@ -217,6 +239,8 @@ class Task extends HTMLElement {
|
||||
if (this.isConcluded) {
|
||||
this.displayCorrectAnswer();
|
||||
}
|
||||
|
||||
this.displayMarking();
|
||||
}
|
||||
}
|
||||
|
||||
@ -258,7 +282,7 @@ class PicturedTask extends Task {
|
||||
this.img.src = data.trim();
|
||||
}
|
||||
break;
|
||||
case "svg":
|
||||
case "html":
|
||||
{
|
||||
this.img = document.createElement("section");
|
||||
this.img.classList.add("question-image");
|
||||
@ -393,6 +417,8 @@ class SingleChoiceTask extends PicturedTask {
|
||||
class OpenEndedTask extends PicturedTask {
|
||||
constructor() {
|
||||
super("openended");
|
||||
|
||||
this.ca_prefix = "<b>Lehetséges megoldások:</b>";
|
||||
}
|
||||
|
||||
createStyle() {
|
||||
@ -400,7 +426,7 @@ class OpenEndedTask extends PicturedTask {
|
||||
|
||||
this.css.innerHTML += `
|
||||
input[type="text"] {
|
||||
font-family: 'Monaco', monospaced;
|
||||
font-family: 'Source Code Pro', monospace;
|
||||
border-width: 0 0 2.2pt 0;
|
||||
background-color: transparent;
|
||||
width: calc(100% - 4em);
|
||||
@ -434,7 +460,9 @@ class OpenEndedTask extends PicturedTask {
|
||||
}
|
||||
|
||||
displayCorrectAnswer() {
|
||||
this.ca_section.innerHTML = "<b>Lehetséges megoldások:</b><br style='margin-bottom: 0.3em'>" + this.correctAnswer.join(", <i>VAGY</i><br>");
|
||||
if (this.correctAnswer !== null) {
|
||||
this.ca_section.innerHTML = this.ca_prefix + "<br style='margin-bottom: 0.3em'>" + this.correctAnswer.join(", <i>VAGY</i><br>");
|
||||
}
|
||||
}
|
||||
|
||||
fromArray(a) {
|
||||
@ -442,12 +470,15 @@ class OpenEndedTask extends PicturedTask {
|
||||
}
|
||||
|
||||
set playerAnswer(player_answer) {
|
||||
if (player_answer === null) {
|
||||
player_answer = "";
|
||||
}
|
||||
super.playerAnswer = player_answer;
|
||||
this.answer_tf.value = player_answer;
|
||||
this.answer_tf.value = this.playerAnswer;
|
||||
}
|
||||
|
||||
get playerAnswer() {
|
||||
return this.player_answer;
|
||||
return super.playerAnswer;
|
||||
}
|
||||
|
||||
updateAnswerFieldState() {
|
||||
@ -489,7 +520,7 @@ class NumberConversionTask extends OpenEndedTask {
|
||||
section#src, section#dst {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
font-family: 'Monaco', monospace;
|
||||
font-family: 'Source Code Pro', monospace;
|
||||
color: #176767;
|
||||
}
|
||||
section#src {
|
||||
@ -574,7 +605,7 @@ class Switch extends HTMLElement {
|
||||
display: inline-block;
|
||||
padding: 0.2em 0.5em;
|
||||
cursor: pointer;
|
||||
font-family: 'Monaco', monospace;
|
||||
font-family: 'Source Code Pro', monospace;
|
||||
}
|
||||
section.button[disabled="false"]:hover {
|
||||
background-color: #408d8d;
|
||||
@ -651,24 +682,12 @@ class Switch extends HTMLElement {
|
||||
}
|
||||
}
|
||||
|
||||
class TruthTableTask extends PicturedTask {
|
||||
constructor() {
|
||||
super("truthtable");
|
||||
|
||||
this.input_variables = [];
|
||||
this.output_variable = "";
|
||||
this.output_switches = [];
|
||||
}
|
||||
|
||||
createStyle() {
|
||||
super.createStyle();
|
||||
|
||||
this.css.innerHTML += `
|
||||
var truth_table_css = `
|
||||
table#tt {
|
||||
border: 1.5pt solid #176767;
|
||||
margin: 0.5em auto;
|
||||
border-spacing: 0;
|
||||
font-family: 'Monaco', monospace;
|
||||
font-family: 'Source Code Pro', monospace;
|
||||
}
|
||||
table#tt tr:not(:last-child) td {
|
||||
border-bottom: 1.2pt solid black;
|
||||
@ -684,6 +703,20 @@ class TruthTableTask extends PicturedTask {
|
||||
border-left: 1.5pt dashed black;
|
||||
}
|
||||
`;
|
||||
|
||||
class TruthTableTask extends PicturedTask {
|
||||
constructor() {
|
||||
super("truthtable");
|
||||
|
||||
this.input_variables = [];
|
||||
this.output_variable = "";
|
||||
this.output_switches = [];
|
||||
}
|
||||
|
||||
createStyle() {
|
||||
super.createStyle();
|
||||
|
||||
this.css.innerHTML += truth_table_css;
|
||||
}
|
||||
|
||||
createElements() {
|
||||
@ -775,6 +808,91 @@ class TruthTableTask extends PicturedTask {
|
||||
}
|
||||
}
|
||||
|
||||
class LogicFunctionTask extends OpenEndedTask {
|
||||
constructor() {
|
||||
super();
|
||||
this.type = "logicfunction";
|
||||
}
|
||||
|
||||
createStyle() {
|
||||
super.createStyle();
|
||||
|
||||
this.css.innerHTML += truth_table_css;
|
||||
this.css.innerHTML += `
|
||||
section#assignment-sec {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
font-family: 'Source Code Pro', monospace;
|
||||
color: #176767;
|
||||
margin-right: 1ch;
|
||||
}
|
||||
input[type="text"] {
|
||||
min-width: 5em;
|
||||
width: 85%;
|
||||
font-family: 'Source Code Pro', monospace;
|
||||
}
|
||||
`;
|
||||
}
|
||||
|
||||
createElements() {
|
||||
super.createElements();
|
||||
|
||||
let assignment_sec = document.createElement("section");
|
||||
assignment_sec.id = "assignment-sec";
|
||||
|
||||
this.answer_container.insertBefore(assignment_sec, this.answer_tf);
|
||||
|
||||
this.assignment_sec = assignment_sec;
|
||||
}
|
||||
|
||||
drawTruthTable() {
|
||||
let N = this.input_variables.length;
|
||||
let M = (1 << N);
|
||||
|
||||
let table = "<table id='tt'>";
|
||||
table += "<tr>";
|
||||
for (let i = 0; i < N; i++) {
|
||||
table += "<th>" + this.input_variables[i] + "</th>";
|
||||
}
|
||||
table += "<th>" + this.output_variable + "</th>";
|
||||
table += "</tr>";
|
||||
for (let i = 0; i < M; i++) {
|
||||
table += "<tr>";
|
||||
for (let j = 0; j < N; j++) {
|
||||
table += "<td>" + ((i >> (N - j - 1)) & 1).toString() + "</td>";
|
||||
}
|
||||
table += "<td>" + this.truth_table.charAt(i) + "</td>"
|
||||
table += "</tr>";
|
||||
}
|
||||
table += "</table>";
|
||||
|
||||
this.imgType = "html";
|
||||
this.imgData = table;
|
||||
}
|
||||
|
||||
fromArray(a) {
|
||||
super.fromArray(a);
|
||||
|
||||
this.input_variables = a["input_variables"];
|
||||
this.output_variable = a["output_variable"];
|
||||
this.truth_table = a["truthtable"];
|
||||
|
||||
this.assignment_sec.innerHTML = `${a["output_variable"]} = `;
|
||||
|
||||
if ((this.truth_table !== undefined) && (this.truth_table !== null) && (this.truth_table !== "")) {
|
||||
this.drawTruthTable();
|
||||
}
|
||||
}
|
||||
|
||||
set correctAnswer(correct_answer) {
|
||||
super.correctAnswer = [ "<code>" + correct_answer + "</code>" ];
|
||||
}
|
||||
|
||||
get correctAnswer() {
|
||||
return super.correctAnswer;
|
||||
}
|
||||
}
|
||||
|
||||
class VerilogTask extends PicturedTask {
|
||||
//static observedAttributes = ["language"]
|
||||
constructor() {
|
||||
@ -920,7 +1038,9 @@ class VerilogTask extends PicturedTask {
|
||||
customElements.define('singlechoice-task', SingleChoiceTask);
|
||||
customElements.define('openended-task', OpenEndedTask);
|
||||
customElements.define('numberconversion-task', NumberConversionTask);
|
||||
customElements.define('truthtable-task', TruthTableTask);
|
||||
customElements.define('slide-switch', Switch);
|
||||
customElements.define('truthtable-task', TruthTableTask);
|
||||
customElements.define('logicfunction-task', LogicFunctionTask);
|
||||
customElements.define('verilog-task', VerilogTask);
|
||||
|
||||
|
||||
|
||||
@ -7,7 +7,7 @@
|
||||
<body>
|
||||
<section style="margin: 0 auto; padding-top: 10ex; text-align: center">
|
||||
<img src="media/maintenance.png" style="width: 16vw">
|
||||
<h3 style="font-family: 'Monaco', monospace"> Az oldal karbantartás alatt áll!</h3>
|
||||
<h3 style="font-family: 'Courier New', monospace"> Az oldal karbantartás alatt áll!</h3>
|
||||
</section>
|
||||
</body>
|
||||
</html>
|
||||
@ -47,9 +47,9 @@ if (!$gameMgr->getGame($game_id)->isUserContributorOrOwner($user_data["nickname"
|
||||
<body>
|
||||
|
||||
<section style="margin-bottom: 0.3em">
|
||||
<input type="text" placeholder="Szűrőfeltétel" id="filter" style="font-family: 'Monaco', monospace; width: 50em;">
|
||||
<input type="text" placeholder="Csoportok" id="groups" style="font-family: 'Monaco', monospace; width: 30em;">
|
||||
<input type="text" placeholder="Rendezés" id="orderby" style="font-family: 'Monaco', monospace; width: 30em;">
|
||||
<input type="text" placeholder="Szűrőfeltétel" id="filter" style="font-family: 'Source Code Pro', monospace; width: 50em;">
|
||||
<input type="text" placeholder="Csoportok" id="groups" style="font-family: 'Source Code Pro', monospace; width: 30em;">
|
||||
<input type="text" placeholder="Rendezés" id="orderby" style="font-family: 'Source Code Pro', monospace; width: 30em;">
|
||||
<input type="button" value="Szűrés" onclick="fetch_results()">
|
||||
<input type="button" value="Jelentés előállítása" onclick="generate_report()">
|
||||
<input type="button" value="Kijelöltek törlése" onclick="delete_tests()"><br>
|
||||
|
||||
@ -135,7 +135,7 @@ span.answer[correct=true] {
|
||||
}
|
||||
|
||||
.terminal-style {
|
||||
font-family: 'Monaco', monospace;
|
||||
font-family: 'Source Code Pro', monospace;
|
||||
width: 40em;
|
||||
font-size: 12pt;
|
||||
}
|
||||
@ -49,7 +49,7 @@ section.answer input[type="radio"]:checked+label:not(.correct-answer) {
|
||||
}
|
||||
|
||||
code {
|
||||
font-family: 'Monaco', monospace;
|
||||
font-family: 'Source Code Pro', monospace;
|
||||
}
|
||||
|
||||
img.question-image {
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
@import url('https://fonts.googleapis.com/css2?family=Autour+One&family=Kanit:wght@500&display=swap');
|
||||
@import url("https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:opsz,wght,FILL,GRAD@24,400,0,0");
|
||||
@import url('https://fonts.googleapis.com/css2?family=JetBrains+Mono:ital,wght@0,100..800;1,100..800&display=swap');
|
||||
@import url('https://fonts.googleapis.com/css2?family=JetBrains+Mono:ital,wght@0,100..800;1,100..800&family=Source+Code+Pro:ital,wght@0,200..900;1,200..900&display=swap');
|
||||
|
||||
body {
|
||||
font-family: 'Autour One', sans-serif;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user