dchadwick/web/modules/custom/ufc/src/Controller/FightTrainingController.php

245 lines
6.2 KiB
PHP
Raw Normal View History

2024-04-09 01:47:04 +00:00
<?php
namespace Drupal\ufc\Controller;
use Drupal\Core\Controller\ControllerBase;
use Drupal\Core\Entity\EntityTypeManager;
use Drupal\node\NodeInterface;
use Drupal\node\Entity\Node;
use Drupal\media\Entity\Media;
use Drupal\file\Entity\File;
use Drupal\Core\Cache\CacheableJsonResponse;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Symfony\Component\HttpFoundation\Request;
class FightTrainingController extends ControllerBase {
/*
* Fields holding fighter data.
*/
protected $fields = [
'age',
'height',
'reach',
'leg_reach',
'knockouts',
'striking_accuracy',
'strikes_per_minute',
'sig_strike_defense',
'absorbed_per_min',
'standing_strikes',
'clinch_strikes',
'ground_strikes',
'grappling_accuracy',
'strikes_to_head',
'strikes_to_body',
'strikes_to_leg',
'knockdown_ratio',
'takedowns_per_15',
'takedown_defense',
'average_fight_time',
'first_round_finishes',
];
/**
* The entity type manager service.
*
* @var Drupal\Core\Entity\EntityTypeManager
* The entity type manager service.
*/
protected $entityTypeManager;
/**
* Public constructor.
*
* @var \Drupal\Core\Entity\EntityTypeManager $entityTypeManager
* The entity type manager service.
*
*/
public function __construct(
EntityTypeManager $entityTypeManager
) {
$this->entityTypeManager = $entityTypeManager;
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container) {
// Instantiates this form class.
return new static(
// Load the service required to construct this class.
$container->get('entity_type.manager'),
);
}
/**
* Generates all fighting data for training NN.
*/
public function generateTrainingData(): CacheableJsonResponse {
// Go get all fights.
$all_fights = $this->entityTypeManager->getStorage('node')->loadByProperties(['type' => 'fight']);
$training_data = [];
foreach ($all_fights as $fight) {
$train_array = [
'input' => [],
'output' => [],
];
// Ensure we have a winner.
if (!$fight->field_result->target_id) {
continue;
}
// Extract fighters.
$fighter_one_id = $fight->field_fighter_one->target_id;
$fighter_two_id = $fight->field_fighter_two->target_id;
if (!$fighter_one_id || !$fighter_two_id) {
continue;
}
$fighter_one_data = $this->getFighterData($fighter_one_id);
$fighter_two_data = $this->getFighterData($fighter_two_id, FALSE);
if (empty($fighter_one_data) || empty($fighter_two_data)) {
continue;
}
$train_array['input'] = array_merge($fighter_one_data, $fighter_two_data);
if ($fight->field_result->target_id == $fighter_one_id) {
$train_array['output'] = [
'fighter_one' => 1,
'fighter_two' => 0,
];
$training_data[] = $train_array;
}
else if ($fight->field_result->target_id == $fighter_two_id) {
$train_array['output'] = [
'fighter_one' => 0,
'fighter_two' => 1,
];
$training_data[] = $train_array;
}
else {
continue;
}
}
return new CacheableJsonResponse($training_data);
}
/**
* Retrieves data about a specific fight for predictions.
*/
public function getFightData(NodeInterface $fight): CacheableJsonResponse {
$fighter_1_id = $fight->field_fighter_one->target_id;
$fighter_2_id = $fight->field_fighter_two->target_id;
$fight_data = array_merge(
$this->getFighterData($fighter_1_id),
$this->getFighterData($fighter_2_id, FALSE),
);
return new CacheableJsonResponse($fight_data);
}
/**
* Gets the fighter data.
*
* @return array<string,mixed>
*/
private function getFighterData(int $id, bool $is_f1 = TRUE): array {
if ($is_f1) {
$prefix = 'fighter_one_';
}
else {
$prefix = 'fighter_two_';
}
$extracted_values = $this->extractValuesFromFields($id, $this->fields, $prefix);
return $this->normalizeData($extracted_values);
}
/**
* Extracts a value from a given field (cannot be ent reference)
*/
private function extractValuesFromFields(int $id, array $field_names, string $prefix): mixed {
$fighter = Node::load($id);
$values = [];
foreach ($field_names as $field) {
$value_key = $prefix . $field;
$field_machine_name = 'field_' . $field;
$values[$value_key] = $fighter->{$field_machine_name}->value ?? 0;
}
return $values;
}
/**
* Normalize the field value.
*
* This needs to be between 0-1
*/
private function normalizeData(array $data): array {
2024-04-14 21:54:51 -07:00
// @todo - this creates an issue where duration of fight is the largest number which isnt really correct
// and smaller duration would be better
//
2024-04-09 01:47:04 +00:00
$min = min(array_values($data));
$max = max(array_values($data));
$normalized = [];
foreach ($data as $key => $value) {
2024-04-14 21:54:51 -07:00
$value = (float) $value;
$normalized[$key] = $this->convertToDecimal($value);
/* $norm_val = 0; */
/* if ($max - $min == 0) { */
/* $normalized[$key] = $norm_val; */
/* } */
/* else { */
/* $norm_val = ($value - $min) / ($max - $min); */
/* $normalized[$key] = $norm_val; */
/* } */
2024-04-09 01:47:04 +00:00
}
return $normalized;
}
2024-04-14 21:54:51 -07:00
public function convertToDecimal(float $input) {
$output = 0;
if ($input > 0 && $input <= 1) {
$output = $input / 100;
}
elseif ($input > 1 && $input < 10) {
$output = $input / 100;
}
elseif ($input > 10 && $input < 100) {
$output = $input / 1000;
}
elseif ($input > 100 && $input < 1000) {
$output = $input / 10000;
}
return $output;
}
2024-04-09 01:47:04 +00:00
/**
* Gets the trained neural network.
*/
public function getNeuralNetwork(): CacheableJsonResponse {
$build = [];
$cur_network = \Drupal::state()->get('neuralNetwork') ?? FALSE;
if (!$cur_network) {
$build['ERROR'] = "There is no spoon.";
}
else {
$build['data'] = base64_decode($cur_network);
}
return new CacheableJsonResponse($build);
}
}