مجموعه رویدادهای *المپیکفناوری* پردیس امسال هم رو به پایان است. این سری رویداد که امسال با شکوه بیشتری نسبت به تمام رویدادهای دیگر برگزار شد قرار است در پایان، مراسمی مجلل برای اهدای مدالها و جوایزی ویژه به نفرات برتر شرکتکننده داشته باشد. یونس مسئول هماهنگی این جشن بزرگ است و از شما که دستیار وی هستید برای انجام کارهای مربوط به جشن کمک میخواهد.
# جزئیات پروژه
شما میتوانید پروژهی اولیه را از [این لینک](/contest/assignments/71158/download_problem_initial_project/252157/) دانلود کنید.
تابعی با نام `handleCeremony` پیادهسازی کنید که آرایهای از افراد را که کلاس `Person` نمایندهای از آنهاست را تحویل میگیرد و سپس این افراد را **به صورت نزولی** مرتبسازی میکند تا لیستی برای مراسم اهدای جوایز آماده شود.
## توضیحات کلاس `Person`
این کلاس نمایندهی افراد شرکتکننده در رویداد *المپیکفناوری* است. ساختار متدها و ویژگیهای کلاس به شکل زیر تعریف میشود:
```php
<?php
class Person
{
public function __construct(
public string $personName,
public int $bootcampScore,
public int $onlineContestScore,
public int $inpersonContestScore,
public float $passionRate,
) {
}
public function calculateTotalScore(): float
{
return ($this->bootcampScore * 0.15) +
($this->onlineContestScore * 0.25) +
($this->inpersonContestScore * 0.5) +
($this->passionRate * 0.1);
}
public function displayInfo(): string
{
$result = "Person Name: " . $this->personName . "\n";
$result .= "Bootcamp Score: " . $this->bootcampScore . "\n";
$result .= "Online Contest Score: " . $this->onlineContestScore . "\n";
$result .= "In-person Contest Score: " . $this->inpersonContestScore . "\n";
$result .= "Passion Rate: " . $this->passionRate . "\n";
$result .= "Total Weighted Score: " . $this->calculateTotalScore() . "\n";
$result .= "- - - - - - - - - - - - -\n";
return $result;
}
}
```
- ویژگی `personName`: این ویژگی به صورت رشتهای از نام فرد شرکتکننده در رویداد است.
- ویژگی `bootcampScore`: این ویژگی به صورت **عدد صحیح مثبت** بیانگر امتیازی است که فرد در بوتکمپهای المپیک فناوری کسب کرده است. **ضریب این ویژگی** $0.15$ **است.**
- ویژگی `onlineContestScore`: این ویژگی به صورت **عدد صحیح مثبت** بیانگر امتیازی است که فرد در مسابقات مجازی المپیک فناوری کسب کرده است. **ضریب این ویژگی** $0.25$ **است.**
- ویژگی `inpersonContestScore`: این ویژگی به صورت **عدد صحیح مثبت** بیانگر امتیازی است که فرد در مسابقات حضوری المپیک فناوری کسب کرده است. **ضریب این ویژگی** $0.5$ **است.**
-ویژگی `passionRate`: این ویژگی به صورت عددی اعشاری بیانگر میزان شور و اشتیاق فرد است که توسط داوران مسابقه تعیین میشود و مقداری بین $0$ تا $10$ دارد. **ضریب این ویژگی** $0.1$ **است.**
- متد `calculateTotalScore`: این متد مجموع وزندار امتیاز فرد را با توجه به ضرایب ویژگیها محاسبه میکند.
- متد `displayInfo`: این متد اطلاعات شرکتکننده را چاپ میکند.
---
شما باید تابع `handleCeremony` را به صورتی پیاده سازی کنید که افراد را بر اساس **مجموع وزندار** امتیازهایشان **به صورت نزولی** مرتبسازی کند. در این بین اگر مجموع وزندار امتیاز دو فرد با یکدیگر **برابر** شد، **اولویت در مقایسه** و رتبهدهی بر اساس ویژگی با **ضریب بیشتر** است. یعنی اگر دو فرد در مجموع امتیاز یکسانی داشته باشند، فردی در مرتبسازی قبلتر ظاهر میشود که امتیاز`inpersonContestScore` او **بزرگتر** باشد. *(در صورتی که این ویژگی نیز در هر دو فرد برابر بود به سراغ امتیاز* `onlineContestScore` *میرویم و به همین شکل...)*
پس از مرتب سازی صرفا باید اطلاعات $5$ **نفر برتر رویداد** *(*$5$ *نفر اول در مرتبسازی انجام شده)* توسط فراخوانی متد `displayInfo` دریافت و در قالب یک رشته خروجی داده شود.
**مقیاسبندی و اعتبارسنجی امتیاز افراد قبلا انجام شده است. همچنین تضمین میشود که تمام امتیازهای هیچ دو فردی با هم برابر نیست.**
امضای تابع `handleCeremony` بهصورت زیر خواهد بود:
```php
function handleCeremony(array $personsArray): string
{
// TODO: Implement
}
```
# مثال
```php
<?php
class Person
{
public function __construct(
public string $personName,
public int $bootcampScore,
public int $onlineContestScore,
public int $inpersonContestScore,
public float $passionRate,
) {
}
public function calculateTotalScore(): float
{
return ($this->bootcampScore * 0.15) +
($this->onlineContestScore * 0.25) +
($this->inpersonContestScore * 0.5) +
($this->passionRate * 0.1);
}
public function displayInfo(): string
{
$result = "Person Name: " . $this->personName . "\n";
$result .= "Bootcamp Score: " . $this->bootcampScore . "\n";
$result .= "Online Contest Score: " . $this->onlineContestScore . "\n";
$result .= "In-person Contest Score: " . $this->inpersonContestScore . "\n";
$result .= "Passion Rate: " . $this->passionRate . "\n";
$result .= "Total Weighted Score: " . $this->calculateTotalScore() . "\n";
$result .= "- - - - - - - - - - - - -\n";
return $result;
}
}
function handleCeremony(array $personsArray): string
{
// TODO: Implement
}
echo handleCeremony([
new Person("Ali Gholi", 1000, 700, 700, 1.5),
new Person("Gholi Ali", 12000, 200, 100, 3.8),
new Person("Ali Phish", 9000, 900, 900, 9.9),
new Person("Pish Ali", 8000, 900, 900, 8.9),
new Person("Ali Reza", 100, 900, 900, 0.3),
new Person("Reza Ali", 18000, 100, 0, 4.4),
new Person("Ali Safar", 10000, 10000, 10000, 0),
new Person("Safar Ali", 8000, 5000, 13100, 0)
]);
```
خروجی موردانتظار:
```plaintext
Person Name: Safar Ali
Bootcamp Score: 8000
Online Contest Score: 5000
In-person Contest Score: 13100
Passion Rate: 0
Total Weighted Score: 9000
- - - - - - - - - - - - -
Person Name: Ali Safar
Bootcamp Score: 10000
Online Contest Score: 10000
In-person Contest Score: 10000
Passion Rate: 0
Total Weighted Score: 9000
- - - - - - - - - - - - -
Person Name: Reza Ali
Bootcamp Score: 18000
Online Contest Score: 100
In-person Contest Score: 0
Passion Rate: 4.4
Total Weighted Score: 2725.44
- - - - - - - - - - - - -
Person Name: Ali Phish
Bootcamp Score: 9000
Online Contest Score: 900
In-person Contest Score: 900
Passion Rate: 9.9
Total Weighted Score: 2025.99
- - - - - - - - - - - - -
Person Name: Gholi Ali
Bootcamp Score: 12000
Online Contest Score: 200
In-person Contest Score: 100
Passion Rate: 3.8
Total Weighted Score: 1900.38
- - - - - - - - - - - - -
```
- همانطور که در خروجی مشاهده میکنید مجموع وزندار امتیاز دو فرد `Safar Ali` و `Ali Safar` با هم **برابر** است اما چون امتیاز `inpersonContestScore` فردی با نام `Safar Ali` **بیشتر** است پس در رتبهبندی **قبل** از فردی با نام `Ali Safar` قرار میگیرد.
# آنچه باید آپلود کنید
یک فایل _PHP_ که تابع `handleCeremony` در آن پیادهسازی شده است آپلود کنید.
علی پس از کسب موفقیتهای فراوان در مسابقات برنامهنویسی *المپیکفناوری* به مجموعه پارک علموفناوری پردیس رفته تا علاوه بر دریافت جوایزش، طبق قولی که به او داده شده بود در یکی از زیرمجموعههای این پارک فناوری استخدام شود. مسئولین این مجموعه برای سنجش تواناییهای علی به او مسئولیت سنگین تصحیح کامل پاسخبرگهای آزمون **کنکور پردیس** را دادند و از او در نهایت علاوه بر اعلام نمرات هر داوطلب، گزارش کاملی از سوالات نیز میخواهند.
# جزئیات پروژه
شما میتوانید پروژهی اولیه را از [این لینک](/contest/assignments/71158/download_problem_initial_project/252156/) دانلود کنید.
تابعی با نام `evaluateQuiz` پیادهسازی کنید که **آرایهای از پاسخهای داوطلبان** در کنکور چهارگزینهای پردیس را همراه با آرایهای از **پاسخهای درست آزمون** دریافت میکند و طبق قوانین و شرایط زیر نمرات هر شخص را مشخص کرده و در نهایت نمرات را همراه با گزارش کاملی از سوالات خروجی میدهد. همچنین شما باید کلاسی به نام `FraudDetection` را پیادهسازی کنید که **متقلبین** را شناسایی کند!
<details class="yellow">
<summary>
**ورودیهای تابع** `evaluateQuiz`
</summary>
## ورودیهای تابع `evaluateQuiz`
- **در اولین ورودی،** این تابع آرایهای دوبعدی *(آرایهای از آرایههای)* پاسخهای مختلف داوطلبان را دریافت میکند.
- هر کدام از این آرایههای پاسخ، شامل مقادیری به صورت **عدد صحیح** و یا $null$ هستند. پاسخهای داوطلبان به سوالات کنکور پردیس به صورت چهارگزینهای است، پس هر خانه از آرایه پاسخ داوطلب باید عددی بین $1$ تا $4$ و یا **بیپاسخ** ($null$) باشد.
- **در دومین ورودی،** این تابع آرایهای از اعداد صحیح را دریافت میکند که هر خانه از آن مشخصکننده گزینهی درست برای سوالی است که شمارهاش با اندیس آن خانه از آرایه برابر است. **توجه داشته باشید که هر سوال دقیقا یک گزینهی درست دارد. تضمین میشود که این مقدار درست برای هر سوال، عدد صحیحی بین** $1$ **تا** $4$ **میباشد.**
- **در سومین ورودی،** این تابع یک نمونه از کلاس کشف تقلب `FraudDetection`را مطابق با توضیحات بخش این کلاس دریافت میکند تا در بدنه کلاس از آن **جهت شناسایی افراد متقلب** استفاده کند.
</details>
<details class="grey">
<summary>
**خروجی تابع** `evaluateQuiz`
</summary>
## خروجی تابع `evaluateQuiz`
خروجی تابع `evaluateQuiz` به صورت رشته ای با ساختار زیر خواهد بود:
```json
{
"scores": [],
"reports": {
"max_score": ,
"min_score": ,
"average_score": ,
"questions_average": [],
"questions_labels": [],
"invalid_answersheets": [],
"cheating_examiners": []
}
}
```
- **خروجی تابع باید به شکل _Json_ باشد.** توجه کنید که خروجی شما حتما باید مطابق با ساختار بالا باشد و در مقادیری مثل کوچگ و بزرگ بودن حروف کلیدها **نباید** تغییری ایجاد شود.
**توضیحات مقادیر خروجی به شکل زیر است:**
- کلید `scores`: آرایهای است از نمراتی که برای کاربران محاسبه شده است. این مقادیر به شکل اعداد صحیح هستند که خانه $i$-ام مشخص کننده نمره کسب شده توسط نفر $i$-ام است. (در صورتی که برگهی فرد خراب باشد، این مقدار $null$ خواهد بود)
- کلید`reports`: این کلید شامل گزارشهای کمی و کیفی آزمون به شکل زیر است:
- کلید `max_score`: مقدار این کلید برابر با بیشترین نمره کسب شده در آزمون است.
- کلید `min_score`: مقدار این کلید برابر با کمترین نمره کسب شده در آزمون است.
- کلید `average_score`: مقدار این کلید برابر با میانگین نمرات کسب شده در آزمون است. **این مقدار به صورت عدد اعشاری با دو رقم اعشار به صورت گرد شده است.** (این مورد باید با استفاده از تابع `round` پیادهسازی شود)
- کلید `questions_average`: مقدار این کلید به صورت آرایهای از اعداد اعشاری با **دو رقم اعشار به صورت گرد** (این مورد باید با استفاده از تابع `round` پیادهسازی شود) شده است که میانگین نمرهای است که افراد مختلف شرکت کننده توانستند از هر کدام از این سوالات کسب کنند. برای مثال اگر پاسخهای افراد به سوال اول به شکل گزینههای `1 1 1 3` باشد و پاسخ سوال گزینه `1` باشد، میانگین نمرهای که کاربران توانستند از این سوال کسب کنند مقدار `2` خواهد بود. (این مورد به صورت `((3 * 3) - 1) / 4 = 2` خواهد بود)
- کلید `questions_labels`: مقدار این کلید به صورت آرایهای از رشتهها است که سطح سوال را مشخص میکند.
- کلید `invalid_answersheets`: مقدار این کلید به صورت آرایهای از اندیس کاربرانی است که پاسخبرگ آنها خراب است. در صورتی که هیچ فردی به این شکل یافت نشد، این مقدار برابر آرایهای خالی خواهد بود.
- کلید `cheating_examiners`: مقدار این کلید به صورت آرایهای از اندیس تمام کاربرانی *(هم شامل افرادی که مراقب آنها را متقلب شناسایی کرده و هم شامل افرادی که الگوریتم کشف تقلب پیادهسازی شده، آنها را متقلب شناسایی کرده)* است که متقلب شناسایی شدند. در صورتی که هیچ فردی به این شکل یافت نشد، این مقدار برابر آرایهای خالی خواهد بود.
</details>
<details class="red">
<summary>
**قوانین و شرایط نمرهدهی در تابع** `evaluateQuiz`
</summary>
## قوانین و شرایط نمرهدهی
- **پاسخ صحیح** به هر سوال $+3$ نمره و **پاسخ غلط** به هر سوال $-1$ نمره به همراه خواهد داشت. پاسخی صحیح است که برابر با پاسخی باشد که در آرایه پاسخ سوالات برای آن سوال مشخص شده است. **تضمین میشود که هر فرد به حداکثر یک گزینه میتواند پاسخ دهد.**
-در صورتی که فرد به سوالی پاسخ نداده باشد *(پاسخ او به این سوال* $null$ *باشد)*، نمرهای برای او لحاظ نمیشود **(نمرهی این سوال** $0$ **لحاظ میگردد)**
- ممکن است به دلیل **اشتباه دستگاه برگهخوان**، مقدار پاسخ برای سوالی از بازه $1$ تا $4$ خارج باشد! برای مثال مقدار پاسخ فردی برای یک سوال مقدار $9$ باشد. د**ر این صورت حتی اگر این اتفاق برای یکی از پاسخهای یک فرد داوطلب رخ دهد، نمرهای برای او نباید محاسبه شود.** نمره نهایی او به صورت $null$ لحاظ میشود تا بعدا مسئولین پارک پردیس، پاسخبرگ او را دوباره تصحیح کنند. **به این پاسخبرگها پاسخبرگهای خراب گفته میشود.**
- پس از مشخص شدن افرادی که پاسخبرگ آنها خراب است، نوبت اجرای تابع `checkResults` برای یافتن افراد متقلب در آزمون است. این تابع باید مطابق با توضیحات داده شده در بخش پیادهسازی این کلاس، لیست افراد متقلب را شناسایی کند. پس از شناسایی افراد متقلب باید نمره نهایی آنها نیز مانند پاسخبرگهای خراب $null$ در نظر گرقته شود.
- افرادی با **پاسخبرگهای خراب و یا افراد متقلب** در گزارشات نهایی ارائه شده **شرکت ندارند** و آمارهای بدست آمده از پاسخهای آنها **نباید** در گزارشدهی نهایی محاسبه شوند.
- سوالات در $4$ سطح با توجه به **نحوه پاسخدهی داوطلبان** به صورت زیر دستهبندی میشوند:
- سطح `Easy`: **بیشتر مساوی** $75$ درصد شرکتکنندگان به آن سوال **پاسخ درست** دادهاند.
- سطح `Medium`: بین $25$ درصد تا $75$ درصد شرکتکنندگان به آن سوال **پاسخ درست** دادهاند.
- سطح `Hard`: **کمتر مساوی** $25$ درصد شرکتکنندگان به آن سوال **پاسخ درست** دادهاند.
- سطح `Brilliant`: **هیچ فردی** در آزمون به این سوال **پاسخ درست** نداده است.
- اگر تمام پاسخبرگها خراب باشند و یا هیچ دادهای از پاسخها یافت نشود (پاسخها $null$ باشند) در این صورت مقادیر استفاده شده در گزارش مقدار $0$ خواهند بود.
</details>
<details class="blue">
<summary>
**ساختار کلاس کشف تقلب** `FraudDetection`
</summary>
کلاس `FraudDetection` برای **کشف تقلبهای آزمون** ساخته شده است. این کلاس در سازنده خود **به ترتیب** درصد حساسیت `sensitivity_percentage` را که عددی بین $0$ و $100$ است را همراه با **آرایهای از اعداد صحیح** که اندیسهای افرادی هستند که مراقب آزمون، آنها را **متقلب** شناسایی کرده است، دریافت میکند. همچنین این کلاس تابعی به نام `checkResults` دارد که با دریافت **آرایهای دوبعدی** از پاسخبرگهای افراد مختلف، با برسی این پاسخبرگها، در صورتی که شخصی وجود داشته باشد که **بیشتر مساوی** درصد حساسیت مشخص شده از پاسخهایش **دقیقا با حداقل یگی از این افراد متقلب یکسان باشد**، آن فرد نیز **متقلب** شناخته خواهد شد. تابع `checkResults` در نهایت آرایهای از اعداد صحیح که **اندیس تمامی افراد متقلب** *(هم شامل افرادی که مراقب آنها را متقلب شناسایی کرده و هم شامل افرادی که الگوریتم کشف تقلب پیادهسازی شده، آنها را متقلب شناسایی کرده)* میباشد را خروجی میدهد.
- کلاس `FraudDetection` به صورت **تزریق وابستگی** *(Dependency Injection)* به تابع نمرهدهی `evaluateQuiz` به عنوان ورودی داده خواهد شد.
به مثالهای زیر از شیوه کارکرد این کلاس، توجه کنید:
## مثال نمونه ۱
```php
$fraud_detector = new FraudDetection(70, [1]);
$answers = [
[1, 1, 1, 1],
[1, 2, null, 2],
[1, 9, 3, 4],
[1, 2, 3, 2],
[1, 2, 3, 1]
];
print_r($fraud_detector->checkResults($answers));
```
```plaintext
Array
(
[0] => 1
[1] => 3
)
```
- در این مثال درصد حساسیت برابر با `70` است و فردی که به عنوان متقلب شناخته شده است فردی با اندیس شماره `1` است. پس از فراخوانی تابع `checkResults` و انجام بررسیهای لازم، به دلیل اینکه پاسخ فردی با اندیس شماره `3` با پاسخ `[1, 2, 3, 2]` در سه چهارم (یا به عبارتی در `75` درصد) پاسخبرگ با پاسخ فرد با اندیس `1` مشابهت دارد پس متقلب شناخته میشود.
## مثال نمونه ۲
```php
$fraud_detector = new FraudDetection(90, [0]);
$answers = [
[1, 2, 3, 4],
[1, 2, 3, 2],
[1, 2, 3, 1]
];
print_r($fraud_detector->checkResults($answers));
```
```plaintext
Array
(
[0] => 0
)
```
- در مثال بالا به دلیل اینکه **هیچ فردی وجود ندارد** که در بیشتر مساوی `90` درصد پاسخبرگ با پاسخبرگ فرد مختلف با اندیس شماره `0` برابر باشد پس تنها فرد متقلب همان اندیس شماره `0` خواهد بود. *(هر دو فرد دیگر در* `75` *درصد از پاسخبرگهایشان با فرد متقلب مشابهت دارند که این مقدار از درصد حساسیت کمتر است)*
</details>
---
امضای تابع `evaluateQuiz` بهصورت زیر خواهد بود:
```php
function evaluateQuiz(array $answers_array, array $key_array, FraudDetection $fraudDetector): string
{
// TODO: Implement
}
```
امضای کلاس `FraudDetection` به صورت زیر خواهد بود:
```php
class FraudDetection
{
public function __construct()
{
// TODO: Implement
}
public function checkResults(array $answers_array): array
{
// TODO: Implement
}
}
```
<details class="green">
<summary>
**مثال و خروجی مورد انتظار** *(حتما مطالعه شود)*
</summary>
# مثال
```php
<?php
class FraudDetection
{
public function __construct()
{
// TODO: Implement
}
public function checkResults(array $answers_array): array
{
// TODO: Implement
}
}
function evaluateQuiz(array $answers_array, array $key_array, FraudDetection $fraudDetector): string
{
// TODO: Implement
}
$answers = [
[1, 1, 1, 1],
[1, 2, null, 2],
[1, 9, 3, 4],
[1, 2, 3, 2],
[1, 2, 3, 1]
];
$keys = [1, 2, 3, 4];
echo evaluateQuiz($answers, $keys, new FraudDetection(70, [1]));
```
خروجی موردانتظار:
```plaintext
{
"scores": [
0,
null,
null,
null,
8
],
"reports": {
"max_score": 8,
"min_score": 0,
"average_score": 4,
"questions_average": [
3,
1,
1,
-1
],
"questions_labels": [
"Easy",
"Medium",
"Medium",
"Brilliant"
],
"invalid_answersheets": [
2
],
"cheating_examiners": [
1,
3
]
}
}
```
- پاسخبرگ داوطلب با اندیس $2$ به دلیل اینکه در یکی از سوالات پاسخی برابر با $9$ دارد به عنوان **پاسخبرگ خراب** در نظر گرفته شده و در محاسبه آمارها نیز سایر پاسخهای این فرد **در نظر گرفته نشده است**.
</details>
# آنچه باید آپلود کنید
یک فایل _PHP_ که تابع `evaluateQuiz` و کلاس `FraudDetection` در آن پیادهسازی شده است آپلود کنید.