Three card brag hand ranking? PHP - php

I'm finding it difficult to find a card ranking tutorial, or even some source code to read off to point me in the right direction.
I'm basically going in the direction of creating multiple functions with multiple in_array And writing them from scratch, as doing this will make it easy for three of a kind. Example
function is_trip($card1, $card2, $card3)
{
if (in_array($card1, $aces) && in_array($card2, $aces) && in_array($card3, $aces))
{
$score = 9500;
$rank = 'Three Aces';
}
if (in_array($card1, $kings) && in_array($card2, $kings) && in_array($card3, $kings))
{
$score = 9000;
$rank = 'Three Kings';
}
} And so on ...
So that would most likely work with trips, but then for a straight flush I would use a method of the way the cards are organized by number, as they're in the array in suit order.
So a straight flush would be hopefully as simple as $highcard + $lowcard / 2 == $midcard if that is true then you have a straight flush.
As for a straight, I'm stuck and would most likely have to use an array with my current mind set but writing that would seem like a lot of code when it is most likely simpler..
And for flushes it wouldn't be difficult to use the in_array as I'd only need to range 1-13 14-26 27-39 40-52 in an in_array to determine a flush, but then I'd need $highcard value $midcard value to also play a role to determine a flush against others.
You may of got to this point and thought, What's his question??
Well, my question is.. Am I going the right way about ranking the cards, should I use a bucket counting method to put the ranks into a bit-code and use a lookup table? Or have you any advice on where I should be heading if my methods of doing it are completely stupid..
Thanks in advance for any help.

It's very rough, and untested, but what about something like: -
<?php
$hand = new Hand;
$hand->addCard(new Card(RankInterface::ACE, SuitInterface::SPADE));
$hand->addCard(new Card(RankInterface::QUEEN, SuitInterface::HEART));
$hand->addCard(new Card(RankInterface::KING, SuitInterface::CLUB));
$isFlush = isFlush($hand);
Using something like: -
<?php
namespace Card;
interface SuitInterface {
const
SPADE = 'spade',
HEART = 'heart',
DIAMOND = 'diamond',
CLUB = 'club';
}
interface RankInterface {
const
JOKER = 0,
ACE = 1,
TWO = 2,
THREE = 3,
FOUR = 4,
FIVE = 5,
SIX = 6,
SEVEN = 7,
EIGHT = 8,
NINE = 9,
TEN = 10,
JACK = 11,
QUEEN = 12,
KING = 13;
}
class Card {
protected
$rank,
$suit;
public function __construct($rank, $suit) {
$this->rank = $rank;
$this->suit = $suit;
}
public function getRank() {
return $this->rank;
}
public function getSuit() {
return $this->suit;
}
public function isSameRank(Card $card) {
return $this->getRank() === $card->getRank();
}
public function isSameSuit(Card $card) {
return $this->getSuit() === $card->getSuit();
}
}
class Hand
{
protected
$storage = array();
public function addCard(Card $card) {
$this->storage[] = $card;
return $this;
}
public function asArray() {
return $this->storage;
}
}
function isFlush(Hand $hand) {
$cards = $hand->asArray();
$first = array_shift($cards);
foreach($cards as $card) {
if( ! $first->isSameSuit($card)) {
return false;
}
}
return true;
}
You would then just need to add some individual pieces of logic for the various valid hands/combos.

Related

Heuristic to sort any "kind" of technical measurements in PHP

I different lists with measurements of the same dimension but a bit mixed units like
"1 m, 200 mm, 1 ft"
or maybe also
"1 °C, 273 K" and so on.
Now I want to sort them by absolute order
"200 mm, 1 ft, 1 m" and "273 K, 1 °C"
I am wondering if this a an already solved problem, as I do not want to reinvent the wheel. I am afraid, this might be some kind of "shopping for PHP extensions" questions, but I already found some helpful packages:
https://github.com/PhpUnitsOfMeasure/php-units-of-measure can do all kind of conversation between units of measure.
I already have created code to separate unit and number.
So what I am thinking, to "brute force" the unit to a certain dimension of those:
https://github.com/PhpUnitsOfMeasure/php-units-of-measure/tree/master/source/PhysicalQuantity
Next I could pick the first dimension and convert everything to the first "main" SI unit and sort it.
Right?
Generally, what you need to do is convert these units to some common measurement, but only for the purpose of sorting.
Use usort() and a custom callback function. In your callback, do the conversion for the purpose of comparison.
Be sure to keep the original unit when returning the result though, or rounding errors will creep in.
That is the solution I came up with, based on the suggestions
public function testCompareLength()
{
$this->assertLessThan(0, $this->objectDe->compareFunction('100 mm', '1 m'));
}
public function testCompareTemperature()
{
$this->assertLessThan(0, $this->objectDe->compareFunction('1 K', '0 °C'));
$this->assertGreaterThan(0, $this->objectDe->compareFunction('0 °C', '1 K'));
$this->assertEquals(0, $this->objectDe->compareFunction('-273 °C', '0 K'));
}
/**
* #param $numberString
*
* #return array
*/
public function parseNumber($numberString): array
{
$values = preg_split('/(?<=[0-9.,])(?=[^0-9,.]+)/i', $numberString);
$float = $values[0];
$unit = $values[1] ?? '';
$decPos = strpos($float, '.');
if ($decPos === false) {
$precision = 0;
} else {
$precision = strlen($float) - $decPos - 1;
}
return ['float' => $float, 'unit' => $unit, 'precision' => $precision];
}
private function heuristicMeasureFactory($measure)
{
$prioritizedDimensions = [
Temperature::class,
Length::class,
];
$unit = trim($measure['unit']);
foreach ($prioritizedDimensions as $class) {
foreach ($class::getUnitDefinitions() as $definition) {
if ($definition->getName() == $unit) {
return new $class($measure['float'], $unit);
}
}
}
// now process aliases
foreach ($prioritizedDimensions as $class) {
foreach ($class::getUnitDefinitions() as $definition) {
foreach ($definition->aliases as $alias) {
if ($alias == $unit) {
return new $class($measure['float'], $unit);
}
}
}
}
return null; // NaN
}
/**
* Sort apples and oranges -- kind of. Not.
*
* Compares two strings which represend a measurement of the same physical dimension
*/
public function compareFunction($a, $b)
{
$definitions = Temperature::getUnitDefinitions();
$aParsed = $this->parseNumber($a);
$aVal = $this->heuristicMeasureFactory($aParsed);
$bParsed = $this->parseNumber($b);
$bVal = $this->heuristicMeasureFactory($bParsed);
if ($aVal == null || $bVal == null) {
return strnatcmp($aVal, $bVal); // fallback to string comparision
}
return bccomp($aVal->subtract($bVal)->toNativeUnit(), 0, 36);
}

How to call a function with other function as argument

im just learning php
Im trying to add a log with comments to my functions output.
Right now it looks like this:
//the function
function add1($x){
if($GLOBALS['logging'] === 'on'){ $log[] = 'Adding 1 to '.$x;};
$a = $x + 1;
if($GLOBALS['logging'] === 'on'){
$return[] = $a;
$return[] = $log;
return $return;
}else{ return $a; };
};
//calling the function
if($GLOBALS['logging'] === 'on'){
$return = add1($x);
$number = $return[0];
$log = $return[1];
}else{ $number = add1($x); };
Im kinda annoyed by the fact i need to retype this if statement.
So i made a seperate function for returning the function
which looks like this:
//function
function log_return($data = 'x', $log = 'x'){
if($GLOBALS['logging'] === 'on'){
if($data !== 'x') $return[] = $data;
if($log !== 'x') $return[] = $log;
return $return;
} return $data;
};//function end
And returning it with:
return $return = isset($log) ? log_return($data, $log) : log_return($data);
Now my quastion is: Is there a way to call a function with function..
like:
call_function(add1($x));
so i can return it either with log or without..
Given the answer https://stackoverflow.com/a/2700760/5387193 - this should work:
function add1($a)
{
// add1 code goes here
}
function call_function($name, $param)
{
$name($param);
}
call_function('add1', $x);
On a side note, your variable and function names aren't very intuitive. Perhaps you should study how to write good quality readable code. I recommend reading chapter 9 of Refactoring by Martin Fowler, it's quite good. You can find a PDF version on the web.
Another note, your return statement return $return = isset($log) ? log_return($data, $log) : log_return($data); has a unnecessary assignment to $return. The code should simply read
return isset($log) ? log_return($data, $log) : log_return($data);
Yes, it is possible. To simplify:
function first($x) {
return $x+1;
}
function second($y) {
return $y+1;
}
echo second(first(1)); // Returns 3, ie. 1+1+1
As gview said in his comment, don't use global variables. Argument lists exist for several reasons, included but not limited to making code easier to read, edit, and debug. The same goes for function and variable names.
Moreover, your code is very messy. It can be consolidated:
function addTo($currentValue, $valueToAdd, $logging = 0)
{
if ($logging) {
logWrite('addTo', "Adding $valueToAdd to $currentValue");
return $currentValue + $valueToAdd;
} else {
return $currentValue;
}
}
function logWrite($operation, $message)
{
$log = getLog(); // maybe it's a file, or DB record or something
// perform the write, depending on your implementation
}
$number = addTo($someStaringValue, $someOtherValue, 1);
All of this said, logging should not control program flow. In other words, whether something is logged by the system or not should have no bearing on what your code is trying to do. I really think you need to take a broader view of what you're trying to do and break it up into components.
At best, your code should tell a logger to log info, and the logger itself should determine if logging is actually turned on. If it is, the info is logged. If not, then the code that calls on the logger still works and goes about its business.

Printing relations between members

I've a university project in which I've to print the relations between students in different classes level by level. The idea is if we have John and Kris studying in the same class they are friends of first level, if Kris studies with Math in same class then John and Math are friends of second level. I researched the problem and I found algorithms like this, but my main problem is that I use objects as input data :
<?php
class Student {
private $id = null;
private $classes = [];
public function __construct($id) {
$this->id = $id;
}
public function getId() {
return $this->id;
}
public function getClasses() {
return $this->classes;
}
public function addClass(UClass $class) {
array_push($this->classes, $class);
}
}
class UClass {
private $id = null;
private $students= [];
public function __construct($id) {
$this->id = $id;
}
public function getId() {
return $this->id;
}
public function getStudents() {
return $this->students;
}
public function addStudent(Student $student) {
array_push($this->students, $student);
$student->addClass($this);
}
}
function getRelations(Student $start_student, &$tree = array(), $level = 2, &$visited) {
foreach ($start_student>Classes() as $class) {
foreach ($class->Students() as $student) {
if($start_student->getId() != $student->getId() && !is_int(array_search($student->getId(), $visited))) {
$tree[$level][] = $student->getId();
array_push($visited, $student->getId());
getRelations($student, $tree, $level+1, $visited);
}
}
}
}
$class = new UClass(1);
$class2 = new UClass(2);
$class3 = new UClass(3);
$student = new Student(1);
$student2 = new Student(2);
$student3 = new Student(3);
$student4 = new Student(4);
$student5 = new Student(5);
$student6 = new Student(6);
$class->addStudent($student);
$class->addStudent($student2);
$class->addStudent($student4);
$class2->addStudentr($student2);
$class2->addStudent($student4);
$class2->addStudent($student5);
$class3->addStudent($student4);
$class3->addStudent($student5);
$class3->addStudent($student6);
$tree[1][] = $student->getId();
$visited = array($student->getId());
getRelations($student, $tree, 2, $visited);
print_r($tree);
I'm stuck at writing getRelations() function that should create an array that is something like
Array ( [1] => Array ( [0] => 1 ) [2] => Array ( [0] => 2 [1] => 4 ) [3] => Array ( [0] => 5 [1] => 6 ) )
but I can't get the recursion right(or probably the whole algorithm). Any help will be greatly appreciated.
The logic in your recursive procedure is not correct. Example:
Say you enter the procedure for some level A and there are actually 2 students to be found for a connection at that level.
You handle the first, assign the correct level A, mark him as "visited".
Then, before getting to the second, you process level A+1 for the first student. Somewhere in his "chain" you may also find the second student that was waiting to get handled at level A. However, he now gets assigned some higher level A+n, and is then marked as visited.
Next, when the recursion for student1 is finished, you continue with the second. However, he has already been "visited"...
(By the way, I do not quite understand (but my php is weak...) why your first invocation of GetRelations specifies level=2.)
Anyway, to get your logic right there's no need for recursion.
Add a property "level" to each student. Put all students also in an overall collection "population".
Then, for a chosen "startStudent", give himself level=0, all other students level=-1.
Iterate levels and try to fill in friendship levels until there's nothing left to do. My php is virtually non-existent, so I try some pseudo-code.
for(int level=0; ; level++) // no terminating condition here
{
int countHandled = 0;
for each (student in population.students)
{
if (student.level==level)
{
for each (class in student.classes)
{
for each (student in class.students)
{
if(student.level==-1)
{
student.level = level+1;
countHandled++;
}
}
}
}
}
if(countHandled==0)
break;
}
Hope this helps you out. Of course, you still have to fill in the tree/print stuff; my contribution only addresses the logic of assigning levels correctly.
I come up with that function(not sure if it's the best solution, but it works with the class objects)
function print_students(Student $start_student, &$tree = array(), $lvl = 1) {
if (!$start_student) {
return;
}
$tree[$lvl][] = $start_student->getId();
$q = array();
array_push($q, $start_student);
$visited = array($start_student->getId());
while (count($q)) {
$lvl++;
$lvl_students = array();
foreach ($q as $current_student) {
foreach ($current_student->getClasses() as $class) {
foreach ($class->getStudents() as $student) {
if (!is_int(array_search($student->getId(), $visited))) {
array_push($lvl_students, $student);
array_push($visited, $student->getId());
$tree[$lvl][] = $student->getId();
}
}
}
}
$q = $lvl_students;
}
}

Accessing class variables from an array

I'm new to PHP and I'm having trouble accessing some class variables.
I have this class:
class emptyClass
{
private $ladderWinsNoCoin = 0;
private $ladderWinsCoin = 0;
private $arenaWinsNoCoin = 0;
private $arenaWinsCoin = 0;
private $ladderLossesNoCoin = 0;
private $ladderLossesCoin = 0;
private $arenaLossesNoCoin = 0;
private $arenaLossesCoin = 0;
public function getProperty($property) {
if (property_exists($this, $property)) {
return $this->$property;
}
}
public function upOne($property) {
if (property_exists($this, $property)) {
$this->$property=($property+1);
}
}
public function setProperty($property, $value) {
if (property_exists($this, $property)) {
$this->$property = $value;
}
}
}
I then create 9 instances of it in an array
/* Create classes in an array so we can call
them with strings from the database*/
$classes = array(
'Druid' => new emptyClass,
'Hunter' => new emptyClass,
'Mage' => new emptyClass,
'Paladin'=> new emptyClass,
'Priest' => new emptyClass,
'Rogue' => new emptyClass,
'Shaman' => new emptyClass,
'Warlock'=> new emptyClass,
'Warrior'=> new emptyClass
);
Next I want to increment the values of the class variables to match data got from a database and this is what I've come up with
foreach ($games as $game){ // Go through the games array from the database
$gameID = current($game); // Unused (for now)
$heroClass = (string)next($game);
$villainClass = (string)next($game); // Unused (for now)
$win = (int)next($game);
$coin = (int)next($game);
$ladder = (int)next($game);
//Add up the values in the class objects
if ($ladder==1&&$coin==1&&$win==1){ // Ladder win with coin
$classes[$heroClass] -> {upOne($ladderWinsCoin)};
} else if ($ladder==1&&$coin==1&&$win==0){ // Ladder loss with coin
$classes[$heroClass] -> {upOne($ladderLossesCoin)};
} else if ($ladder==1&&$coin==0&&$win==1){ // Ladder win without coin
$classes[$heroClass] -> {upOne($ladderWinsNoCoin)};
} else if ($ladder==1&&$coin==0&&$win==0){ // Ladder loss without coin
$classes[$heroClass] -> {upOne($ladderLossesNoCoin)};
} else if ($ladder==0&&$coin==1&&$win==1){ // Arena win with coin
$classes[$heroClass] -> {upOne($arenaLossesCoin)};
} else if ($ladder==0&&$coin==1&&$win==0){ // Arena loss with coin
$classes[$heroClass] -> {upOne($arenaLossesCoin)};
} else if ($ladder==0&&$coin==0&&$win==1){ // Arena win without coin
$classes[$heroClass] -> {upOne($arenaWinsNoCoin)};
} else if ($ladder==0&&$coin==0&&$win==0){ // Arena loss without coin
$classes[$heroClass] -> {upOne($arenaLossesNoCoin)};
}
Where $game is an array from the database that looks something like this
[1, 'Mage', 'Druid', 1, 0, 1]
When it runs I get a fatal error
PHP Fatal error: Call to undefined function setProperty() in /home/vooders/public_html/gameReader.php on line 48
Edit:
So after trying renaming the getter/setter I'm still getting the fatal error, so now I'm sure its how I'm calling the objects.
I'll try to talk you through my thinking
$classes[$heroClass] -> {upOne($ladderWinsCoin)};
If we take this line above, $heroClass will be a string from the database in this example 'Mage'.
Now I want to use this string to call the right object from the $classes array then increment the appropriate variable by 1.
Categorically, I would advise you not to "write code external to a class that knows the class's business."
"I am a class. Therefore, I am alive. Tell me what has happened and I shall respond accordingly. Ask me what you want to know and I shall provide you with the answer. But: Do Not Meddle in the Affairs of Classes, for you are crunchy and taste good with worcestershire sauce!!"
(1) Don't put your sticky fingers on the class's variables "from outside." Tell the Class what has happened, that it may increment or decrement its own properties. (You did say they were private, didn't you? As they should be.)
(2) If you want to know "an answer," which may be based on the value of one or many properties, according to simple or complex logic, then the Class should contain that logic, as it pertains "to itself."
//it should work now
<?
class emptyClass {
private $ladderWinsNoCoin = 5; private $ladderWinsCoin = 0; private $arenaWinsNoCoin = 0; private $arenaWinsCoin = 0;
private $ladderLossesNoCoin = 0; private $ladderLossesCoin = 0; private $arenaLossesNoCoin = 0; private $arenaLossesCoin = 0;
public function getProperty($property) {
if (property_exists($this, $property)) {
return $this->$property;
}
}
public function setProperty($property, $value) {
if (property_exists($this, $property)) {
$this->$property = $value;
}
}
public function upOne($property) {
if (property_exists($this, $property)) {
$this->$property++;
}
}
}
$classes = array(
'Druids' => new emptyClass,
'Elfos' => new emptyClass
);
$classes["Druids"]->setProperty("ladderWinsNoCoin",50);
echo $classes["Druids"]->getProperty("ladderWinsNoCoin") . "<br>";
$classes["Druids"]->upOne("ladderWinsNoCoin");
echo $classes["Druids"]->getProperty("ladderWinsNoCoin"). "<br>";
$classes["Elfos"]->setProperty("ladderLossesCoin",25);
echo $classes["Elfos"]->getProperty("ladderLossesCoin"). "<br>";
$classes["Elfos"]->upOne("ladderLossesCoin");
echo $classes["Elfos"]->getProperty("ladderLossesCoin"). "<br>";
//50
//51
//25
//26
?>

PHP Recursive Function to get n generations of x parents

I need to return family data (parents, siblings and partners) for 'x' number of generations (passed as $generations parameter) starting from a single person (passed as $id parameter). I can't assume two parents, this particular genealogy model has to allow for a dynamic number of parents (to allow for biological and adoptive relationships). I think my recursion is backwards, but I can't figure out how.
The code below is triggering my base clause 5 times, once for each generation, because $generation is being reduced by 1 not for every SET of parents but for every parent. What I want is for the base clause ($generations == 0) to only be triggered once, when 'x' number of generations for all parents of the initial person are fetched.
public function fetchRelationships($id = 1, $generations = 5, $relationships = array())
{
$perId = $id;
if ($generations == 0) {
return $relationships;
} else {
$parents = $this->fetchParents($perId);
$relationships[$perId]['parents'] = $parents;
$relationships[$perId]['partners'] = $this->fetchPartners($perId);
if (!empty($parents)) {
--$generations;
foreach ($parents as $parentRel) {
$parent = $parentRel->getPer2();
$pid = $parent->getId();
$relationships[$perId]['siblings'][$pid] = $this->fetchSiblings($perId, $pid);
$perId = $pid;
$relationships[$perId] = $this->fetchRelationships($perId, $generations, $relationships);
}
}
return $relationships;
}
}
The methods fetchPartners, fetchParents and fetchSiblings just fetch the matching entities. So I am not pasting them here. Assuming that there are 2 parents, 5 generations and each generation has 2 parents then the return array should contain 62 elements, and should only trigger the base clause once those 62 elements are filled.
Thanks, in advance, for any help.
-----------Edit--------
Have rewritten with fetchSiblings and fetchPartners code removed to make it easier to read:
public function fetchRelationships($id = 1, $generations = 5, $relationships = array())
{
$perId = $id;
if ($generations == 0) {
return $relationships;
} else {
$parents = $this->fetchParents($perId);
$relationships[$perId]['parents'] = $parents;
if (!empty($parents)) {
--$generations;
foreach ($parents as $parentRel) {
$perId = $parentRel->getPer2()->getId();
$relationships[$perId] = $this->fetchRelationships($perId, $generations, $relationships);
}
}
return $relationships;
}
}
Garr Godfrey got it right. $generations will equal zero when it reaches the end of each branch. So you'll hit the "base clause" as many times as there are branches. in the foreach ($parents as $parentRel) loop, you call fetchRelationships for each parent. That's two branches, so you'll have two calls to the "base clause". Then for each of their parents, you'll have another two calls to the "base clause", and so on...
Also, you're passing back and forth the relationships, making elements of it refer back to itself. I realize you're just trying to retain information as you go, but you're actually creating lots of needless self-references.
Try this
public function fetchRelationships($id = 1, $generations = 5)
{
$perId = $id;
$relationships = array();
if ($generations == 0) {
return $relationships;
} else {
$parents = $this->fetchParents($perId);
$relationships[$perId]['parents'] = $parents;
if (!empty($parents)) {
--$generations;
foreach ($parents as $parentRel) {
$perId = $parentRel->getPer2()->getId();
$relationships[$perId] = $this->fetchRelationships($perId, $generations);
}
}
return $relationships;
}
}
you'll still hit the base clause multiple times, but that shouldn't matter.
you might be thinking "but then i will lose some of the data in $relationships", but you won't. It's all there from the recursive returns.
If you're pulling this out of a database, have you considered having the query do all of the leg work for you?
Not sure how you need the data stacked or excluded, but here's one way to do it:
<?php
class TreeMember {
public $id;
// All three should return something like:
// array( $id1 => $obj1, $id2 => $obj2 )
// and would be based on $this->$id
public function fetchParents(){ return array(); }
public function fetchPartners(){ return array(); };
public function fetchSiblings(){ return array(); };
public function fetchRelationships($generations = 5)
{
// If no more to go
if ($generations == 0) { return; }
$branch = array();
$branch['parents'] = $this->fetchParents();
$branch['partners'] = $this->fetchPartners();
$branch['partners'] = $this->fetchSiblings();
// Logic
$generations--;
foreach($branch as $tmType, $tmArr)
{
foreach($tmArr as $tmId => $tmObj)
{
$branch[$tmType][$tmId] =
$mObj->fetchRelationships
(
$generations
)
);
});
return array($this->id => $branch);
}
}

Categories