6

I had a bit of a problem to come up with a good title, but I'll explain now.

I'm building an online game. I'm trying to build option to destroy weapons.

I have 4 kinds of weapons - attack,defence,patrol and spy. patrol and spy weapons have 3 ranks of weapons, and attack and defence have 15 ranks.

I have a table for each of the categories with col. named w1,w2,w3,w4... and ID of the user ofcourse.

I gave each rank points, so w15 for example worth 15 points, and w2 worth 2 points, and I built a function who calculate how much points the attacker destroyed to the defender.

Where I'm stuck is how to pick randomly weapons?

let's say the attacker destroyed 100 points worth of weapons. so it can be 100 weapons of rank 1 of patrol, or 25 weapons rank 1 of each category, or 10 weapons ranked 10. I need it to be randomly between categories (attack,defence,patrol and spy) and between weapons (w1,w2,w3..). In addition I need it to be in the limit of the number of weapons the defender have, he can't lose more then he have.

Thank you very much !! I know I wrote a long question

OfirH
  • 651
  • 1
  • 8
  • 19
  • 3
    I would consider a schema change. Why not a single weapons table, with a weapon type column which relates to a table containing details on different weapons types? – Mike Brant Oct 14 '14 at 13:39
  • 3
    Can you please post a schema and sample data, with the desired output? – Neville Kuyt Oct 14 '14 at 13:59
  • How much columns w1..w15 do you have? Please provide schema in sql fiddle. – Mateusz Nowak Oct 14 '14 at 14:19
  • There is a specific method of providing schema? because I don't know it. – OfirH Oct 14 '14 at 14:21
  • there is two table with 16 col. ID and w1...w15, and two tablem with 4 col. ID and w1,w2,w3. – OfirH Oct 14 '14 at 14:22
  • 2
    You can post the schema with the create scripts, some sample data in a table format and the desired output in table format. It is a good practice to post the create scripts AND create an [SQL fiddle](http://sqlfiddle.com) demo with the relevant tables and sample data. – Pred Oct 14 '14 at 15:03
  • k... so this is almost an obvious [change counting/making](http://stackoverflow.com/questions/14992411/understanding-change-making-algorithm) problem. Let's say there's 30 ways to come up with 100 points destroyed from whatever data are in your tables. How do you want to choose? Just entirely random or weighted in someway? Is it okay to have all one of one type/category? – gloomy.penguin Oct 15 '14 at 02:15
  • Can you edit the question and provide sample data and desired results? It is unclear from the description exactly what you want returned. – Gordon Linoff Oct 15 '14 at 07:05

6 Answers6

2

First UNION your four tables this way

SELECT * FROM (
  SELECT * FROM w1
  UNION
  SELECT * FROM w2
  UNION
  SELECT * FROM w3
  UNION
  SELECT * FROM w4
)

then calculate your weight-function and do a random pick

... ORDER BY RAND() LIMIT 5;
Benvorth
  • 7,416
  • 8
  • 49
  • 70
2
// randomly picks a number between 1 and 4
$randomWeapon = rand(1,4);

// Creates for ex. SELECT * FROM w1
$selectWeapon = mysqli($con, "SELECT * FROM w$randomWeapon"
Menno van der Krift
  • 303
  • 1
  • 3
  • 15
1

Based on my other hint here is a solution that should do the trick.

Basically you first randomize all waepons with their weights of a user. If you have NULLvalues here you should de-select them. The huge LIMIT after the ORDER BY RAND() is necessary because I found that the MySQL Optimizer will remove the randomization if you dont specify a LIMIT here. Just make it bigger than the whole tables will ever get.

Second you sum the weapons weight consecutive by a running variable.

Third you pick from the whole consecutive list the amount of damage done. You should adapt that to a range of hitpoints if the required damage is not matched exactly.

-- 3. limit your sum to hitpoints
SELECT * FROM (
    -- 2. now sum weaponWeight
    SELECT weaponWeight, @prevWeight, @prevWeight:= weaponWeight + @prevWeight as cumulSum 
    FROM (
        -- 1. shuffle all waepons of a user to do the random pick
        SELECT weaponWeight FROM
        (
            -- attack
            SELECT w1 as weaponWeight FROM attack WHERE ID = 'myUserID'
            UNION
            SELECT w2 as weaponWeight FROM attack WHERE ID = 'myUserID'
            UNION
            ...
            UNION
            SELECT w15 as weaponWeight FROM attack WHERE ID = 'myUserID'

            -- defence
            UNION
            SELECT w1 as weaponWeight FROM defence WHERE ID = 'myUserID'
            UNION
            ...
            UNION
            SELECT w15 as weaponWeight FROM defence WHERE ID = 'myUserID'

            -- patrol
            UNION
            SELECT w1 as weaponWeight FROM patrol WHERE ID = 'myUserID'
            UNION
            SELECT w2 as weaponWeight FROM patrol WHERE ID = 'myUserID'
            UNION
            SELECT w3 as weaponWeight FROM patrol WHERE ID = 'myUserID'

            -- spy
            UNION
            SELECT w1 as weaponWeight FROM spy WHERE ID = 'myUserID'
            UNION
            SELECT w2 as weaponWeight FROM spy WHERE ID = 'myUserID'
            UNION
            SELECT w3 as weaponWeight FROM spy WHERE ID = 'myUserID'
        )
        ORDER BY RAND() 
        LIMIT 1000000000000 -- huge number here because Optimizer will remove random order otherwise
    ) as randomizedData,
    (SELECT @prevWeight := 0) as a 
) as sums
WHERE round(sums.cumulSum) = 100
Benvorth
  • 7,416
  • 8
  • 49
  • 70
1

I think this is more of a logic question than it is a technical question about dealing with tables. If you handle the logic first then you can easily perform needed actions on the table however you'd like. We just need to know how many of each item would be used (destroyed) with a random selection. Here is a random selection method in part sudo-code / part php:

1. Query database for available items and their relative values.
2. Store information as a multi-dimensional Array
3. Shuffle the Array
 //in php 
    bool shuffle ( array $itemsArray() )
4. Iterate through each item in the array and add 1
   to a variable for that item if, we have not reached our
   limiting factors (amount available and cost vs remaining points).
   Do this until all available points are allotted to a variable.
 //in php
    $i = 0;   
    do {

      if ($itemsArray[$i][numAvail] > 0 &&         
         ($availiblePoints - $itemsArray[$i][cost] >= $itemsArray[$i][cost]){   
            $$itemsArray[$i]++
            //use of '$$' for variable variable  
            $availiblePoints-=$itemsArray[$i][cost];
      }
      else {
      countSkips++ 
     //need to keep track of how many items we skip
     //if $availiblePoints is not zero yet but skips is size of array then 
     //we are done and have leftover points that cant be used.
      }  
         $i++;
      if ($i > count($itemsArray)) { $i=0; };
      //start over if we have gone past the end of our Array

   } while ($availiblePoints > 0 && $countSkips < count($itemsArray) );
5.  Logic Done. Now use the new variables to perform action on tables

Because the array was randomly shuffled, our results are random no matter how many points we have. If we have 100 points and the first item in our randomized array cost 100 points, or the first 4 cost 25 points; randomness will have done its job either way.

This is just the concept. The code can be improved a bunch, for instance the variables we saved should actually be in an array so we can loop through them when its time to perform actions on the table.

Eric Jones
  • 134
  • 1
  • 1
  • 12
0

Okay... I tested with one table logical table. Just combine (union) all the things you want to include. (question is tagged with php, so this is a php solution....)

<?php

$weapons = array(
      array('name'=>'knife',        'type'=>'A', 'weight'=>5),
      array('name'=>'sword',        'type'=>'A', 'weight'=>6),
      array('name'=>'axe',          'type'=>'A', 'weight'=>3),
      array('name'=>'handgun',      'type'=>'B', 'weight'=>7),
      array('name'=>'rifle',        'type'=>'B', 'weight'=>5),
      array('name'=>'cannon',       'type'=>'B', 'weight'=>2),
      array('name'=>'mustard gas',  'type'=>'C', 'weight'=>7),
      array('name'=>'agent orange', 'type'=>'C', 'weight'=>10),
      array('name'=>'lewisite',     'type'=>'C', 'weight'=>5),
      array('name'=>'mind',         'type'=>'D', 'weight'=>8),

      // must have at least one thing with one... for this to work. 
      // i can definitely work on a solution that doesn't require this 
      // but it would take me a minute to think about it... 

      array('name'=>'words',        'type'=>'D', 'weight'=>1),  
      array('name'=>'hands',        'type'=>'D', 'weight'=>2),
      array('name'=>'silent treatment','type'=>'D', 'weight'=>5),
   ); 

$total_destroyed = 100;

$return = get_weapons($weapons, $weapons, $total_destroyed);

print_r($return);


function get_weapons($orig_weapons, $in_weapons, $n) {
   // filter for only weapons w/ weight less than $n
   $in_weapons = array_filter($in_weapons, 
                             array(new LowerThanFilter($n), 
                             'isLowerOrEq'));

   $return = array(); 

   if ($n > 0) {
      if (empty($in_weapons)) { 
         $return = get_weapons($orig_weapons, $orig_weapons, $n); 
      }
      else { 
         $found_it = array();  
         for ($i = 0; $i < count($in_weapons); $i++) {
            $rand_index  = array_rand($in_weapons);
            $rand_weapon = $in_weapons[$rand_index];
            if ($rand_weapon['weight'] <= $n) { 
               break;
            }
         }    
         $max_ct = floor($n/$rand_weapon['weight']);

         $weapon_ct = rand(1,$max_ct);
         $amount    = $weapon_ct * $rand_weapon['weight'];

         unset($in_weapons[$rand_index]);

         $get_more = get_weapons($orig_weapons, $in_weapons, $n-$amount); 

         $return   = $get_more;
         $return[] = array_merge($rand_weapon, array(
                                 'count' =>$count, 
                                 'amount'=>$amount));  
      }  
   }  
   return $return;
} 

class LowerThanFilter {
   // http://stackoverflow.com/a/5483168/623952
   private $num; 
   function __construct($num) {
      $this->num = $num;
   } 
   function isLowerOrEq($i) {
      return $i['weight'] <= $this->num;
   }
}

?>

sample output, which can easily be re-arranged. the index value refers to the original $weapons array index.

Array
(
    [0] => Array
        (
            [name] => words
            [type] => D
            [weight] => 1
            [count] => 1
            [amount] => 1
        )

    [1] => Array
        (
            [name] => knife
            [type] => A
            [weight] => 5
            [count] => 2
            [amount] => 10
        )

    [2] => Array
        (
            [name] => sword
            [type] => A
            [weight] => 6
            [count] => 1
            [amount] => 6
        )

    [3] => Array
        (
            [name] => agent orange
            [type] => C
            [weight] => 10
            [count] => 2
            [amount] => 20
        )

    [4] => Array
        (
            [name] => mustard gas
            [type] => C
            [weight] => 7
            [count] => 9
            [amount] => 63
        )

)
gloomy.penguin
  • 5,833
  • 6
  • 33
  • 59
0

Try something like:

    $attackWeapon = array( /* weapon info comes here  */   );
    $defenseWeapon = array( /* weapon info comes here  */);
    $patrolWeapon = array( /* weapon info comes here  */);
    $spyWeapon = array( /* weapon info comes here  */);


    $rdmWeapon = mt_rand(1,4);

    $attackLenght = count($attackWeapon);
    $rdmAttackWeapon = mt_rand(0, $attackLenght-1);

    $defenseLenght = count($defenseWeapon);
    $rdmDefenseWeapon = mt_rand(0, $defenseLenght-1);

    $patrolLenght = count($patrolWeapon);
    $rdmPatrolWeapon = mt_rand(0, $patrolLenght-1);

    $spyLenght = count($spyWeapon);
    $rdmSpyLenght = mt_rand(0, $spyLenght-1);


    If($rdmWeapon=1){
    $dropWeapon = "attack";
    echo  $rdmAttackWeapon;
    }
    else if($rdmWeapon=2){
      $dropWeapon = "defense";
    echo $rdmDefenseWeapon;
    }
    else if($rdmWeapon=3){
      $dropWeapon = "patrol";
      echo $rdmPatrolWeapon
    }
    else($rdmWeapon=4){
      $dropWeapon = "spy";
      echo $rdmSpyWeapon;
    }

It´s simple and goofy, but works for me.