mirror of
https://github.com/ronitsinha/almia-randomizer.git
synced 2026-09-01 07:24:35 -05:00
354 lines
10 KiB
C++
354 lines
10 KiB
C++
#include "pokedatastructure.h"
|
|
|
|
#define RED 1
|
|
#define BLACK 0
|
|
|
|
using namespace std;
|
|
|
|
PokeDataStructure::PokeDataStructure()
|
|
{
|
|
root = nullptr;
|
|
}
|
|
|
|
|
|
PokeDataStructure::~PokeDataStructure()
|
|
{
|
|
// walk tree in post-order traversal and delete
|
|
post_order_delete(root);
|
|
root = nullptr; // not really necessary, since the tree is going
|
|
// away, but might want to guard against someone
|
|
// using a pointer after deleting
|
|
}
|
|
|
|
void PokeDataStructure::post_order_delete(TreeNode* node)
|
|
{
|
|
if (node == nullptr) return; // Empty tree
|
|
|
|
post_order_delete(node->left);
|
|
post_order_delete(node->right);
|
|
|
|
delete node;
|
|
}
|
|
|
|
void PokeDataStructure::print_current_level (TreeNode* node, int level)
|
|
{
|
|
if (node == nullptr) return;
|
|
|
|
if (level == 1) {
|
|
if (is_leaf(node))
|
|
cout << "[" << node->data << ":" << node->poke_ids.size() << "] ";
|
|
else {
|
|
int num_children = (node->left == nullptr or node->right == nullptr) ? 1 : 2;
|
|
cout << "(" << node->data << " : " << num_children << ") ";
|
|
}
|
|
} else if (level > 1) {
|
|
print_current_level(node->left, level-1);
|
|
print_current_level(node->right, level-1);
|
|
}
|
|
}
|
|
|
|
void PokeDataStructure::print_level_order()
|
|
{
|
|
int h = height(root);
|
|
for (int i = 1; i <= h; i++) {
|
|
cout << "LEVEL: " << i << endl;
|
|
print_current_level(root, i);
|
|
cout << endl;
|
|
}
|
|
}
|
|
|
|
bool PokeDataStructure::is_leaf (TreeNode *node) {
|
|
if (node == nullptr) return false;
|
|
|
|
return (node->left == nullptr and node->right == nullptr);
|
|
}
|
|
|
|
int PokeDataStructure::height(TreeNode* node)
|
|
{
|
|
if (node == nullptr) return 0;
|
|
|
|
return node->height;
|
|
}
|
|
|
|
int PokeDataStructure::get_balance(TreeNode* node) {
|
|
if (node == nullptr) return 0;
|
|
|
|
return height(node->left) - height(node->right);
|
|
}
|
|
|
|
// https://www.geeksforgeeks.org/insertion-in-an-avl-tree/#
|
|
PokeDataStructure::TreeNode* PokeDataStructure::left_rotate(TreeNode *x) {
|
|
TreeNode *y = x->right;
|
|
TreeNode *T2 = y->left;
|
|
|
|
y->left = x;
|
|
x->right = T2;
|
|
|
|
x->height = get_max(height(x->left), height(x->right)) + 1;
|
|
y->height = get_max(height(y->left), height(y->right)) + 1;
|
|
|
|
return y;
|
|
}
|
|
|
|
PokeDataStructure::TreeNode* PokeDataStructure::right_rotate(TreeNode *y) {
|
|
TreeNode *x = y->left;
|
|
TreeNode *T2 = x->right;
|
|
|
|
x->right = y;
|
|
y->left = T2;
|
|
|
|
x->height = get_max(height(x->left), height(x->right)) + 1;
|
|
y->height = get_max(height(y->left), height(y->right)) + 1;
|
|
|
|
return x;
|
|
}
|
|
|
|
PokeDataStructure::TreeNode* PokeDataStructure::add_node(TreeNode *node,
|
|
uint16_t field_signature, uint16_t poke_id)
|
|
{
|
|
if (node == nullptr) {
|
|
// data always stored in the leaves
|
|
TreeNode *new_node = new TreeNode;
|
|
new_node->left = new_node->right = nullptr;
|
|
new_node->height = 1;
|
|
new_node->data = field_signature;
|
|
new_node->poke_ids.push_back(poke_id);
|
|
|
|
return new_node;
|
|
}
|
|
|
|
if (is_leaf(node)) {
|
|
// make a new node with two leaves
|
|
if (node->data == field_signature) {
|
|
node->poke_ids.push_back(poke_id);
|
|
return node;
|
|
}
|
|
|
|
TreeNode *new_leaf = new TreeNode;
|
|
new_leaf->left = new_leaf->right = nullptr;
|
|
new_leaf->height = 1;
|
|
new_leaf->data = field_signature;
|
|
new_leaf->poke_ids.push_back(poke_id);
|
|
|
|
TreeNode *new_root = new TreeNode;
|
|
new_root->left = new_root->right = nullptr;
|
|
new_root->height = 2;
|
|
|
|
if (field_signature < node->data) {
|
|
new_root->left = new_leaf;
|
|
new_root->right = node;
|
|
new_root->data = field_signature;
|
|
} else {
|
|
new_root->left = node;
|
|
new_root->right = new_leaf;
|
|
new_root->data = node->data;
|
|
}
|
|
|
|
return new_root;
|
|
}
|
|
|
|
if (field_signature > node->data)
|
|
node->right = add_node(node->right, field_signature, poke_id);
|
|
else
|
|
node->left = add_node(node->left, field_signature, poke_id);
|
|
|
|
node->height = 1 + get_max(height(node->left), height(node->right));
|
|
|
|
int balance = get_balance(node);
|
|
|
|
// 4 cases for rebalancing AVL tree
|
|
while (balance > 1 or balance < -1) {
|
|
|
|
// Left-Left
|
|
if (balance > 1 and field_signature < node->left->data)
|
|
node = right_rotate(node);
|
|
|
|
// Right-Right
|
|
else if (balance < -1 and field_signature > node->right->data)
|
|
node = left_rotate(node);
|
|
|
|
// Left-Right
|
|
else if (balance > 1 and field_signature > node->left->data) {
|
|
node->left = left_rotate(node->left);
|
|
node = right_rotate(node);
|
|
}
|
|
|
|
// Right-Left
|
|
else if (balance < -1 and field_signature < node->right->data) {
|
|
node->right = right_rotate(node->right);
|
|
node = left_rotate(node);
|
|
}
|
|
|
|
|
|
balance = get_balance(node);
|
|
field_signature = node->data;
|
|
}
|
|
|
|
// no unbalance; return unchanged node
|
|
return node;
|
|
|
|
}
|
|
|
|
void PokeDataStructure::add_pokemon(uint16_t poke_id, uint16_t name_id, uint8_t field_id,
|
|
uint8_t field_level)
|
|
{
|
|
pokemon_reals_to_names[poke_id] = name_id;
|
|
|
|
if (pokemon_field_moves.find(name_id) != pokemon_field_moves.end())
|
|
return; // already seen this pokemon
|
|
|
|
uint16_t field_signature = ((uint16_t) field_id << 8) | field_level;
|
|
|
|
// assert(field_id == (field_signature >> 8));
|
|
// assert(field_level == (uint8_t) field_signature);
|
|
|
|
pokemon_field_moves[name_id] = field_signature;
|
|
pokemon_names_to_reals[name_id] = poke_id;
|
|
root = add_node(root, field_signature, name_id);
|
|
}
|
|
|
|
pair<uint8_t, uint8_t> PokeDataStructure::get_field_move(uint16_t poke_id) {
|
|
uint16_t field_signature = pokemon_field_moves[pokemon_reals_to_names[poke_id]];
|
|
|
|
uint8_t field_move = (uint8_t)(field_signature >> 8);
|
|
// https://stackoverflow.com/questions/27889213/c-integer-downcast
|
|
// downcast truncates most significant bytes
|
|
uint8_t field_level = (uint8_t) field_signature;
|
|
|
|
return pair<uint8_t, uint8_t>(field_move, field_level);
|
|
}
|
|
|
|
vector<uint16_t> PokeDataStructure::get_pokemon_with_geq_field_move(
|
|
uint16_t poke_id) {
|
|
assert (pokemon_reals_to_names.find(poke_id) != pokemon_reals_to_names.end());
|
|
uint16_t field_signature = pokemon_field_moves[pokemon_reals_to_names[poke_id]];
|
|
uint16_t field_sig_max = field_signature | 0xFF;
|
|
|
|
// the reason we store name ids as opposed to the actual pokemon ids
|
|
// is because name ids are unique and we want to avoid duplicates
|
|
// unfortunately this means we need maps to go from name ids to real ids
|
|
// and back.
|
|
vector<uint16_t> query = range_query(field_signature, field_sig_max);
|
|
vector<uint16_t> answer;
|
|
|
|
// get real ids from name ids
|
|
for (auto it = query.begin(); it != query.end(); ++it)
|
|
answer.push_back(pokemon_names_to_reals[*it]);
|
|
|
|
return answer;
|
|
}
|
|
|
|
void PokeDataStructure::collect_subtree(TreeNode *node, vector<uint16_t> *vec)
|
|
{
|
|
if (node == nullptr) return;
|
|
|
|
if (is_leaf(node)) {
|
|
vec->insert(vec->end(), node->poke_ids.begin(), node->poke_ids.end());
|
|
return;
|
|
}
|
|
|
|
collect_subtree(node->left, vec);
|
|
collect_subtree(node->right, vec);
|
|
}
|
|
|
|
|
|
PokeDataStructure::TreeNode* PokeDataStructure::find_vsplit (TreeNode *node,
|
|
uint16_t min, uint16_t max)
|
|
{
|
|
if (node == nullptr) return nullptr;
|
|
|
|
if (node->data > min and node->data > max)
|
|
return find_vsplit(node->left, min, max);
|
|
|
|
if (node->data < min and node->data < max)
|
|
return find_vsplit(node->right, min, max);
|
|
|
|
return node;
|
|
}
|
|
|
|
// TODO: report in order (not really necessary...)
|
|
vector<uint16_t> PokeDataStructure::range_query(uint16_t min, uint16_t max) {
|
|
vector<uint16_t> results;
|
|
|
|
TreeNode* v_split = find_vsplit(root, min, max);
|
|
|
|
if (v_split == nullptr) return results;
|
|
|
|
if (is_leaf(v_split)) {
|
|
results.insert(results.end(), v_split->poke_ids.begin(),
|
|
v_split->poke_ids.end());
|
|
return results;
|
|
}
|
|
|
|
// get all right subtrees on path to min
|
|
TreeNode *min_path = v_split->left;
|
|
while (min_path != nullptr) {
|
|
if (is_leaf(min_path)) {// leaf
|
|
if (min_path->data >= min and min_path->data <= max) {
|
|
results.insert(results.end(), min_path->poke_ids.begin(),
|
|
min_path->poke_ids.end());
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (min_path->data >= min) {
|
|
collect_subtree(min_path->right, &results);
|
|
min_path = min_path->left;
|
|
} else
|
|
min_path = min_path->right;
|
|
}
|
|
|
|
// get all left subtrees on path to max
|
|
TreeNode *max_path = v_split->right;
|
|
while (max_path != nullptr) {
|
|
if (is_leaf(max_path)) {// leaf
|
|
if (max_path->data >= min and max_path->data <= max) {
|
|
results.insert(results.end(), max_path->poke_ids.begin(),
|
|
max_path->poke_ids.end());
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (max_path->data < max) {
|
|
collect_subtree(max_path->left, &results);
|
|
max_path = max_path->right;
|
|
} else
|
|
max_path = max_path->left;
|
|
}
|
|
|
|
return results;
|
|
}
|
|
|
|
void PokeDataStructure::self_test () {
|
|
for (auto it = pokemon_field_moves.begin(); it != pokemon_field_moves.end(); ++it) {
|
|
vector<uint16_t> manually_checked;
|
|
|
|
uint8_t field_move = (uint8_t) (it->second >> 8);
|
|
uint8_t field_level = (uint8_t) it->second;
|
|
|
|
for (auto it2 = pokemon_field_moves.begin(); it2 != pokemon_field_moves.end(); ++it2) {
|
|
uint8_t field_move2 = (uint8_t) (it2->second >> 8);
|
|
uint8_t field_level2 = (uint8_t) it2->second;
|
|
|
|
if (field_move2 == field_move and field_level2 >= field_level)
|
|
manually_checked.push_back(it2->first);
|
|
}
|
|
|
|
vector<uint16_t> range_query = get_pokemon_with_geq_field_move(it->first);
|
|
|
|
sort(manually_checked.begin(), manually_checked.end());
|
|
sort(range_query.begin(), range_query.end());
|
|
|
|
assert(range_query.size() == manually_checked.size());
|
|
|
|
for (unsigned int i = 0; i < range_query.size(); i ++) {
|
|
assert(range_query[i] == manually_checked[i]);
|
|
}
|
|
|
|
}
|
|
|
|
cout << "self-test passed." << endl;
|
|
}
|