Skip to content

Commit

Permalink
Change generation so that randomness is decided on the front end. The…
Browse files Browse the repository at this point in the history
… back end determines all eligible Pokemon and can be cached.
  • Loading branch information
nerdydrew committed Dec 20, 2016
1 parent 309aab5 commit f3e2b8b
Show file tree
Hide file tree
Showing 10 changed files with 191 additions and 156 deletions.
2 changes: 1 addition & 1 deletion .gitignore
@@ -1,5 +1,5 @@
/config.php /config.php
/analytics.js /config.js
/error_log /error_log


.ftpquota .ftpquota
Expand Down
9 changes: 1 addition & 8 deletions .htaccess
Expand Up @@ -3,14 +3,6 @@ RewriteEngine on
RewriteCond %{HTTP_HOST} ^www\.randompokemon\.com [NC] RewriteCond %{HTTP_HOST} ^www\.randompokemon\.com [NC]
RewriteRule (.*) http://randompokemon.com/$1 [R=301,L] RewriteRule (.*) http://randompokemon.com/$1 [R=301,L]


# Redirect /random/ to random.php
RewriteRule ^random/?$ /random.php [QSA,L]

# Prevent hotlinking http://www.htaccesstools.com/hotlink-protection/
#RewriteCond %{HTTP_REFERER} !^$
#RewriteCond %{HTTP_REFERER} !^http(s)?://(www\.)?randompokemon.com [NC]
#RewriteRule \.(jpg|jpeg|png|gif)$ - [NC,F,L]

# Cache sprites and other goodies # Cache sprites and other goodies
ExpiresActive on ExpiresActive on
ExpiresByType image/png "access plus 1 year" ExpiresByType image/png "access plus 1 year"
Expand All @@ -19,6 +11,7 @@ ExpiresByType image/gif "access plus 1 year"
ExpiresByType image/ico "access plus 1 year" ExpiresByType image/ico "access plus 1 year"
ExpiresByType text/css "access plus 1 month" ExpiresByType text/css "access plus 1 month"
ExpiresByType application/javascript "access plus 1 month" ExpiresByType application/javascript "access plus 1 month"
ExpiresByType text/html "access plus 1 day"


# Hide the /.git/ and /setup/ directories from being accessed online. # Hide the /.git/ and /setup/ directories from being accessed online.
RedirectMatch 404 /\.git RedirectMatch 404 /\.git
Expand Down
2 changes: 1 addition & 1 deletion index.php
Expand Up @@ -21,7 +21,7 @@
<link rel="mask-icon" href="/safari-pinned-tab.svg" color="#FF4A4A"> <link rel="mask-icon" href="/safari-pinned-tab.svg" color="#FF4A4A">
<meta name="theme-color" content="#FFFFFF"> <meta name="theme-color" content="#FFFFFF">


<script src="analytics.js"></script> <script src="config.js"></script>
<script> <script>
logToAnalytics(); logToAnalytics();
</script> </script>
Expand Down
9 changes: 9 additions & 0 deletions pokemon_list.php
@@ -0,0 +1,9 @@
<?php
// This page reads in $_GET URL parameters, validates them, determines which
// Pokemon fulfill the parameters, and outputs the result in JSON.

require_once 'config.php';
require_once 'utils.php';

$params = validate_parameters($_GET);
echo json_encode($params->get_list());
170 changes: 150 additions & 20 deletions random.js
Expand Up @@ -10,34 +10,99 @@ function generateRandom() {
var region = document.getElementById('region').value; var region = document.getElementById('region').value;
var type = document.getElementById('type').value; var type = document.getElementById('type').value;


var url = "random?n="+n+"&ubers="+ubers+"&nfes="+nfes+"&natures="+natures+"&sprites="+sprites+"&forms="+forms+"&region="+region+"&type="+type; var url = "pokemon_list.php?n=" + n + "&ubers=" + ubers + "&nfes=" + nfes + "&natures=" + natures + "&sprites=" + sprites + "&forms=" + forms + "&region=" + region + "&type=" + type;


if (window.XMLHttpRequest) { var xmlhttp = new XMLHttpRequest();
var xmlhttp = new XMLHttpRequest();
} else {
// code for IE6, IE5
var xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
}


xmlhttp.onreadystatechange = function() { xmlhttp.onreadystatechange = function() {
if (xmlhttp.readyState == 1) { if (xmlhttp.readyState == 1) {
document.getElementById("controls").className = "loading"; document.getElementById("controls").className = "loading";
} }
if (xmlhttp.readyState == 4 && xmlhttp.status == 200) { if (xmlhttp.readyState == 4 && xmlhttp.status == 200) {
document.getElementById("controls").className = ""; document.getElementById("controls").className = "";
document.getElementById("results").innerHTML = htmlifyListOfPokemon(xmlhttp.responseText); var randomPokemon = generateRandomPokemon(xmlhttp.responseText);
document.getElementById("results").innerHTML = htmlifyListOfPokemon(randomPokemon);
logToAnalytics(url); logToAnalytics(url);
} }
}; };
xmlhttp.open("GET", url, true); xmlhttp.open("GET", url, true);
xmlhttp.send(); xmlhttp.send();
} }


function generateRandomPokemon(eligiblePokemonJson) {
var eligiblePokemon = JSON.parse(eligiblePokemonJson);
var n = document.getElementById('n').value;
var randomIndices = generateDistinctRandomNumbers(eligiblePokemon.length, n);
var forms = document.getElementById('forms').checked;

// Keep track so that only one Pokemon can be mega.
var canBeMega = forms;

// Generate n random Pokemon.
var randomPokemon = [];
randomIndices.forEach(function(index) {
var pokemon = eligiblePokemon[index];
if (forms && pokemon.forms) {
// Pick a random form. Avoid megas, if possible.
pokemon = getRandomForm(pokemon.forms, false);

if (pokemon.is_mega == 1) {
canBeMega = false;
}
}

randomPokemon.push(pokemon);

});

if (forms && canBeMega) {
// Choose a mega if one hasn't already been generated.
var potentialIndices = getPotentialMegaIndices(eligiblePokemon, randomIndices);
if (potentialIndices.length > 0) {
var chosenIndex = getRandomElement(potentialIndices);
var chosenPokemon = eligiblePokemon[randomIndices[chosenIndex]];
randomPokemon[chosenIndex] = getRandomForm(chosenPokemon.forms, true);
}
}

return randomPokemon;
}

// Returns a list of indices for the given pokemonList of Pokemon that have mega forms.
function getPotentialMegaIndices(eligiblePokemon, randomIndices) {
var indices = [];
for (var i=0; i<randomIndices.length; i++) {
var forms = eligiblePokemon[randomIndices[i]].forms;
if (forms) {
forms.forEach(function(form) {
if (form.is_mega == 1) {
indices.push(i);
}
})
}
}
return indices;
}

// Returns a random form from the list. Either tries to get or avoids getting a mega,
// depending on favorMegas. An example where we can't avoid a mega: if an Ampharos
// is generated for Dragon types.
function getRandomForm(forms, favorMegas) {
var preferredMegaValue = favorMegas ? 1 : 0;
var nonMegaForms = forms.filter(function(form) {
return (form.is_mega == preferredMegaValue);
});
if (nonMegaForms.length > 0) {
return getRandomElement(nonMegaForms);
} else {
return getRandomElement(forms);
}
}

// Converts a JSON array of Pokémon into an HTML ordered list. // Converts a JSON array of Pokémon into an HTML ordered list.
function htmlifyListOfPokemon(generatedPokemonJson) { function htmlifyListOfPokemon(generatedPokemon) {
var generatedPokemon = JSON.parse(generatedPokemonJson);
var output = '<ol>'; var output = '<ol>';
for (i=0;i<generatedPokemon.length; i++) { for (i=0; i<generatedPokemon.length; i++) {
output += htmlifyPokemon(generatedPokemon[i]); output += htmlifyPokemon(generatedPokemon[i]);
} }
output += '</ol>'; output += '</ol>';
Expand All @@ -47,26 +112,91 @@ function htmlifyListOfPokemon(generatedPokemonJson) {


// Converts JSON for a single Pokémon into an HTML list item. // Converts JSON for a single Pokémon into an HTML list item.
function htmlifyPokemon(pokemon) { function htmlifyPokemon(pokemon) {
var title = (pokemon.shiny) ? 'Shiny ' + pokemon.name : pokemon.name; // http://bulbapedia.bulbagarden.net/wiki/Shiny_Pok%C3%A9mon#Generation_VI
var shiny = Math.floor(Math.random() * 65536) < 16;
var sprites = document.getElementById('sprites').checked;
var natures = document.getElementById('natures').checked;

var title = shiny ? 'Shiny ' + pokemon.name : pokemon.name;


if (pokemon.sprite) { if (sprites) {
var out = '<li title="' + title + '">'; var out = '<li title="' + title + '">';
} else { } else {
var out = '<li class="imageless">'; var out = '<li class="imageless">';
} }


if (pokemon.nature) { if (natures) {
out += '<span class="nature">' + pokemon.nature + "</span> "; out += '<span class="nature">' + generateNature() + "</span> ";
} }
out += pokemon.name; out += pokemon.name;
if (pokemon.shiny) { if (shiny) {
out += ' <span class="shiny">&#9733;</span>'; out += ' <span class="shiny">&#9733;</span>';
} }
if (pokemon.sprite) { if (sprites) {
out += '<div class="wrapper"><img src="' + pokemon.sprite + '" alt="' + title + '" title="' + title + '" /></div>'; var sprite = getSpritePath(pokemon, shiny);
out += '<div class="wrapper"><img src="' + sprite + '" alt="' + title + '" title="' + title + '" /></div>';
} }


out += '</li>'; out += '</li>';


return out; return out;
} }

function getSpritePath(pokemon, shiny) {
var path = shiny ? PATH_TO_SHINY_SPRITES : PATH_TO_SPRITES;
var name = pokemon.id;
if (pokemon.sprite_suffix) {
name = name + '-' + pokemon.sprite_suffix;
}
return path + name + SPRITE_EXTENTION;
}

function generateNature() {
return getRandomElement(natures_list);
}

var natures_list = ['Adamant', 'Bashful', 'Bold', 'Brave', 'Calm', 'Careful', 'Docile', 'Gentle', 'Hardy', 'Hasty', 'Impish', 'Jolly', 'Lax', 'Lonely', 'Mild', 'Modest', 'Na&iuml;ve', 'Naughty', 'Quiet', 'Quirky', 'Rash', 'Relaxed', 'Sassy', 'Serious', 'Timid'];

// Generates up to n random numbers from [0, range).
function generateDistinctRandomNumbers(range, n) {
if (range > 10 * n) { // 10 is an arbitrarily chosen value
return generateDistinctRandomNumbersLarge(range, n);
} else {
return generateDistinctRandomNumbersSmall(range, n);
}
}

// Generate distinct random numbers where the possible range is closer
// to the number of elements (n) to generate.
function generateDistinctRandomNumbersSmall(range, n) {
// Instantiate array of valid numbers
var valid_numbers = [];
for (var i=0; i<range; i++) {
valid_numbers.push(i);
}

var generated_numbers = [];
while (generated_numbers.length < n && valid_numbers.length > 0) {
var random_index = Math.floor(Math.random()*valid_numbers.length);
generated_numbers.push(valid_numbers[random_index]);
valid_numbers.splice(random_index, 1);
}
return generated_numbers;
}

// Generate distinct random numbers where the possible range is much
// larger than the number of elements (n) to generate.
function generateDistinctRandomNumbersLarge(range, n) {
var numbers = [];
while(numbers.length < n) {
var random = Math.floor(Math.random() * range);
if (numbers.indexOf(random) < 0) {
numbers.push(random);
}
}
return numbers;
}

function getRandomElement(arr) {
return arr[Math.floor(Math.random()*arr.length)];
}
14 changes: 0 additions & 14 deletions random.php

This file was deleted.

8 changes: 0 additions & 8 deletions setup/analytics.js

This file was deleted.

14 changes: 14 additions & 0 deletions setup/config.js
@@ -0,0 +1,14 @@
// This method is used for analytics. Leave its body blank otherwise.
function logToAnalytics(url) {
if (typeof url === "undefined") {
// Log the current page
} else {
// Log the URL provided
}
}

// These directories should contain sprites named by PokéDex ID
// without leading zeros (e.g. "25.gif").
define('PATH_TO_ANIMATED_SPRITES', '/sprites/animated/');
define('PATH_TO_SHINY_ANIMATED_SPRITES', '/sprites/animated/shiny/');
define('ANIMATED_SPRITE_EXTENTION', '.gif');
14 changes: 0 additions & 14 deletions setup/config.php
Expand Up @@ -3,17 +3,3 @@
define('SQL_DATABASE', ''); define('SQL_DATABASE', '');
define('SQL_USERNAME', ''); define('SQL_USERNAME', '');
define('SQL_PASSWORD', ''); define('SQL_PASSWORD', '');

// These directories should contain sprites named by PokéDex ID
// without leading zeros (e.g. "25.gif").
// These directories should contain sprites named by PokéDex ID
// without leading zeros (e.g. "25.gif").
define('PATH_TO_ANIMATED_SPRITES', '/sprites/animated/');
define('PATH_TO_SHINY_ANIMATED_SPRITES', '/sprites/animated/shiny/');
define('ANIMATED_SPRITE_EXTENTION', '.gif');

define('PATH_TO_REGULAR_SPRITES', '/sprites/');
define('PATH_TO_SHINY_REGULAR_SPRITES', '/sprites/shiny/');
define('REGULAR_SPRITE_EXTENTION', '.png');

define('DEFAULT_SPRITE', '/sprites/0.png');

0 comments on commit f3e2b8b

Please sign in to comment.