В этой статье я расскажу про мой способ расчета приоритета комбинаций в техасском холдеме (покере с 2 карманными картами и 5 общими). Условно разделю статью на 6 частей: Создание массива карт, Расчет Стрита, Расчет Флеша и Стрит Флеша, Расчет Парных комбинаций, Окончательный расчет, Вывод на экран. Код пишу в процедурном стиле.
Цель: получить на выходе программы удобочитаемый результат, доступный для дебаггинга и отладки еще на стадии написания кода. Для достижения поставленной цели делаю разницу между комбинациями в 1e+2 (100) раз.
Примерный вид того, что должно получиться в итоге, где 2 последние карты считаю карманными, а первые 5 общими:

В колоде 4 масти и 13 достоинств, поэтому массив из 7 чисел буду формировать в диапазоне [2 — 14, 102 — 114, 202 — 214, 302 — 314], а далее оперировать остатками от деления на 100 (%100) этих чисел. Можно, конечно, формировать массив из чисел в диапазоне [2 — 14, 22 — 34, 42 — 54, 62 — 74], а затем сравнивать остатками от деления на 20 (%20). Но удобочитаемость первого варианта по-моему на лицо, поэтому остановлюсь на нем.
Стрит — это комбинация, где достоинства карт идут по порядку. Поэтому нет смысла учитывать при расчете все карты, составляющие данную комбинацию, достаточно лишь первой по старшинству карты.
Существует несколько вариантов определения и расчета комбинации Стрит. Думаю, большинство из них сводится к задаче отсортировать остатки от деления членов исходного массива в порядке убывания, а затем каким-либо образом выделить элементы, которые составляю комбинацию (в случае, когда более 5 карт составляют стрит, нужно выбрать наибольший).
Суть моего метода определения стрита сводится к тому, что после сортировки я буду проверять сначала первые пять карт на стрит, затем вторые и третьи. Также не забываю что туз, двойка, тройка, четверка и пятерка тоже составляю стрит. Поэтому, если в наборе есть туз (значение 14), к массиву добавляю восьмой элемент — 1. И следовательно в этом случае нужно проверить четвертую пятерку карт.
Флеш — комбинация из карт одной масти. При расчете нужно учитывать все карты, составляющие комбинацию.
При расчете стрит флеша, как и при расчете стрита, можно учитывать только старшую карту комбинации.
Чтобы определить флеш, пробегусь циклом по массиву. В цикле создам 4 вспомогательных массива (по количеству мастей). В перый массив положу все карты первой масти, во второй — второй и т.д. Если какой-то масти нет, соответствующий массив будет пустой. Если количество элементов массива больш�� либо равно 5, значит данная масть образовала флеш.
Флеш более приоритетен, чем стрит, поэтому именно в функции флеша логично проверить не образовала ли комбинация стрит флеш. Также, думаю, понятно, что неудобно писать отдельную функцию, которая проверяет и стрит, и флеш. Лучше написать функцию, которая проверяет массив на флеш, и в ней же вызвать функцию, которая проверяет флеш на стрит.
Если флеш не образует стрит флеш, отсортирую остатки от деления на 100 (%100) исходного массива. Далее умножу первые пять членов полученного массива на 1е+10, 1е+8, 1е+6, 1е+4, 1е+2 соответственно.
Функцию по определению стрит флеша построю на основе функции по определению стрита, с той лишь разницей, что нужно учесть, что в функцию может прийти массив с количеством элементов меньше 7 (я ведь туда отправляю только те карты, которые образуют флеш).
Функция для определения Флеша:
Функция для определения Стрит Флеша (аналогична Стриту, только нужно учесть, что количество элементов массива может быть не только 7, но еще и 5, и 6, и что возвращаемый результат этой функции должен быть больше):
Существует пять парных комбинаций: каре (4 карты одного достоинства), фулл хаус (3 карты одного достоинства и 2 карты другого достоинства), тройка (3 карты одного достоинства), две пары (2 карты одного достоинства и 2 карты другого достоинства), пара (2 карты одного достоинства).
Важно при расчете помнить, что в комбинации фулл хаус главным является сочетание из трех карт, а из двух второстепенным, т.е., например, комбинацию из 3-х двоек и 2-х десяток будем считать, как 3*1e+12 + 10*1e+10, а не по достоинству карт.
А также при расчете парных комбинаций нужно учитывать проблемы:
1. То, что более 5 карт образуют комбинацию.
2. То, что у нас может возникнуть ситуация, при которой комбинаций более одной.
Для подсчета совпадения карт буду использовать три счетчика, которые будут считать парные карты в колоде. Там, где счетчик равен 1 — участвуют 2 карты в комбинации, где счетчик 2 — 3 карты, где 3 — 4 карты.
Рассмотрим все возможные варианты сочетания счетчиков:
100 — пара
110 — 2 пары
111 — 3 пары (нужно преобразовать к виду 110 (две пары))
200 — тройка
210 — фулл хаус
120 — фулл хаус (нужно преобразовать к виду 210 (фулл хаус))
211 (121, 112) — фулл хаус + пара (нужно преобразовать к виду 210 (фулл хаус))
220 — 2 тройки или фулл хаус + 1 карта (нужно преобразовать к виду 210 (фулл хаус))
300 — каре
310 (130) — каре + пара (нужно преобразовать к виду 300 (каре))
320 (230) — каре + тройка (нужно преобразовать к виду 300 (каре))
Далее произведу окончательный расчет парных комбинаций.
Теперь у нас имеются функции, которые проверяют массив карт на стрит, флеш, стрит флеш и парные комбинации. Вызову их вместе и посчитаю окончательный результат, не забываю также и про расчет старшей карты.
Расчет старшей карты:
1. Определю наибольшую и наименьшие по приоритетности карманные карты. Для этого сравню их остатками от деления на 100 (%100).
2. К наибольшей по прио��итетности карте добавлю наименьшую по приоритетности деленную на 100.
Выводила я результат в браузер, используя вот такой спрайт (брала картинку с сайта: fondhristianin.ru/?p=2941), как background-image для div.

Ширину и высоту для div задавала равными размеру одной карты, т.е. если размер изображения 572px*328px (как в данном случае), а в ширину количество карт равно 13, в высоту — 5, ширину и высоту задавала 44px*65.6px. Далее меняла background-position с учетом ранее сформированного массива карта.
Расчет background-position для любого из divов по оси х:
где $arrayCards — ранее сформированный массив.
$i — порядковый номер карты.
Пояснения к расчету:
В ряду 13 карт, начало 1-ой карты — 0%, начало 13-ой карты — 100%, поэтому разница позиций — 100% / 12 (а не 13). Приводим числа карт из вида [2-14, 102-114, 202-214, 302-314] к [0-12]. Для этого возьмет остаток от числа карты и отнимем от остатка 2. Умножим полученное число на разницу позиций.
Расчет background-position для любого из divов по оси y:
Пояснение к расчету:
В ряду 5 карт, начало 1-ой карты — 0%, начало 5-ой карты — 100%, поэтому разница позиций — 100% / 4 (а не 5). Приводим числа карт из вида [2-14, 102-114, 202-214, 302-314] к [0-3]. Для этого разделим число карты на 100 и округлим в меньшую сторону (можно использовать и простую операцию округления, она сработает аналогичным образом). Умножим полученное число на разницу позиций.
Цель: получить на выходе программы удобочитаемый результат, доступный для дебаггинга и отладки еще на стадии написания кода. Для достижения поставленной цели делаю разницу между комбинациями в 1e+2 (100) раз.
Примерный вид того, что должно получиться в итоге, где 2 последние карты считаю карманными, а первые 5 общими:

Создание массива карт
В колоде 4 масти и 13 достоинств, поэтому массив из 7 чисел буду формировать в диапазоне [2 — 14, 102 — 114, 202 — 214, 302 — 314], а далее оперировать остатками от деления на 100 (%100) этих чисел. Можно, конечно, формировать массив из чисел в диапазоне [2 — 14, 22 — 34, 42 — 54, 62 — 74], а затем сравнивать остатками от деления на 20 (%20). Но удобочитаемость первого варианта по-моему на лицо, поэтому остановлюсь на нем.
function cardsCreation() { $arrayCards = []; for ($i = 0; $i < 7; $i++) { $card = mt_rand(2, 14); // создаю случайное число в диапозоне от 2 до 14 $multiplier = mt_rand(0, 3); //формирую случайным образом множитель if (1 == $multiplier) { //если множитель равен 1, добавляю к числу 100 $card = $card + 100; } else if (2 == $multiplier) { //если множитель равен 2, добавляю к числу 200 $card = $card + 200; } else if (3 == $multiplier) { //если множитель равен 3, добавляю к числу 300 $card = $card + 300; } if (!in_array($card, $arrayCards)) { //проверяю, есть ли в массиве такое число, если нет добавляю его в массив $arrayCards = array_merge($arrayCards, [$card]); } else { // если число есть в массиве, откатываю цикл, чтобы сформировать новое число $i--; } } return $arrayCards; }
Определение и расчет комбинации Стрит
Стрит — это комбинация, где достоинства карт идут по порядку. Поэтому нет смысла учитывать при расчете все карты, составляющие данную комбинацию, достаточно лишь первой по старшинству карты.
Существует несколько вариантов определения и расчета комбинации Стрит. Думаю, большинство из них сводится к задаче отсортировать остатки от деления членов исходного массива в порядке убывания, а затем каким-либо образом выделить элементы, которые составляю комбинацию (в случае, когда более 5 карт составляют стрит, нужно выбрать наибольший).
Суть моего метода определения стрита сводится к тому, что после сортировки я буду проверять сначала первые пять карт на стрит, затем вторые и третьи. Также не забываю что туз, двойка, тройка, четверка и пятерка тоже составляю стрит. Поэтому, если в наборе есть туз (значение 14), к массиву добавляю восьмой элемент — 1. И следовательно в этом случае нужно проверить четвертую пятерку карт.
function straight(array $arrayCards) { $newArrayCards = []; $ace = false; foreach ($arrayCards as $arrayCard) { $newArrayCards = array_merge($newArrayCards, [$arrayCard % 100]); //создаю массив с остатками от деления на 100 if(14 == $arrayCard % 100) { //проверка на туз для комбинации А, 2, 3, 4, 5 $ace = true; } } if($ace == true) { $newArrayCards = array_merge($newArrayCards, [1]); //если в массиве присутствует туз, добавляю к массиву 1 } rsort($newArrayCards); //сортирую массив с числами в порядке убывания $count = 0; //счетчик, к которому добавляется 1 в случае, если разница между текущим элементом цикла и предыдущим = 1 $length = 4; //число, показывает до какого элемента массива проверять $result = 0; //окончательный результат $begin = 0; //число показывает, с какого элемента массива проверять for($i = 1; $i <= $length; $i++) { if (-1 == ($newArrayCards[$i] - $newArrayCards[$i - 1])) { $count++; } if($length == $i) { if(4 == $count) { //$count == 4, когда стрит $result = $newArrayCards[$begin] * 1e+8; } else if($length < 6 or (6 == $length and $ace == true)) {//если стрита нет, идем еще на один круг $length++; //увеличиваем число, до которого будем проверять $begin++; //увеличиваем число, с которого будем проверять $i = $begin; //при попадании в for к $i автоматически добавится 1 ($i++ в for) $count = 0; //обнуляю счетчик } } } return $result; }
Определение и расчет комбинаций Флеш и Стрит Флеш
Флеш — комбинация из карт одной масти. При расчете нужно учитывать все карты, составляющие комбинацию.
При расчете стрит флеша, как и при расчете стрита, можно учитывать только старшую карту комбинации.
Чтобы определить флеш, пробегусь циклом по массиву. В цикле создам 4 вспомогательных массива (по количеству мастей). В перый массив положу все карты первой масти, во второй — второй и т.д. Если какой-то масти нет, соответствующий массив будет пустой. Если количество элементов массива больш�� либо равно 5, значит данная масть образовала флеш.
Флеш более приоритетен, чем стрит, поэтому именно в функции флеша логично проверить не образовала ли комбинация стрит флеш. Также, думаю, понятно, что неудобно писать отдельную функцию, которая проверяет и стрит, и флеш. Лучше написать функцию, которая проверяет массив на флеш, и в ней же вызвать функцию, которая проверяет флеш на стрит.
Если флеш не образует стрит флеш, отсортирую остатки от деления на 100 (%100) исходного массива. Далее умножу первые пять членов полученного массива на 1е+10, 1е+8, 1е+6, 1е+4, 1е+2 соответственно.
Функцию по определению стрит флеша построю на основе функции по определению стрита, с той лишь разницей, что нужно учесть, что в функцию может прийти массив с количеством элементов меньше 7 (я ведь туда отправляю только те карты, которые образуют флеш).
Функция для определения Флеша:
function pokerFlush(array $arrayCards) { $suit1 = []; //первая масть $suit2 = []; //вторая масть $suit3 = []; //третья масть $suit4 = []; //четвертая масть foreach ($arrayCards as $arrayCard) { //создаю 4 массива, содержащих разные масти исходного массива if($arrayCard >= 2 and $arrayCard <= 14) { $suit1 = array_merge($suit1, [$arrayCard]); } else if($arrayCard >= 102 and $arrayCard <= 114) { $suit2 = array_merge($suit2, [$arrayCard]); } else if($arrayCard >= 202 and $arrayCard <= 214) { $suit3 = array_merge($suit3, [$arrayCard]); } else { $suit4 = array_merge($suit4, [$arrayCard]); }} if(count($suit1) >= 5) { //если количество карт первой масти больше или равно 5 $result = straightFlush($suit1); //проверяю не образует ли данная комбинация стрит флеш if(0 == $result) {//если стрит флеша нет foreach ($suit1 as $key1 => $s1) {//выбираю остатки от деления на 100 $suit1[$key1] = $s1 % 100; } rsort($suit1); //сортирую массив по убыванию $result = $suit1[0] * 1e+10 + $suit1[1] * 1e+8 + $suit1[2] * 1e+6 + $suit1[3] * 1e+4 + $suit1[4] * 1e+2; } } else if (count($suit2) >= 5) { //если количество карт второй масти больше или равно 5 $result = straightFlush($suit2); if(0 == $result) { foreach ($suit2 as $key2 => $s2) { $suit2[$key2] = $s2 % 100; } rsort($suit2); $result = $suit2[0] * 1e+10 + $suit2[1] * 1e+8 + $suit2[2] * 1e+6 + $suit2[3] * 1e+4 + $suit2[4] * 1e+2; } } else if (count($suit3) >= 5) { //если количество карт третьей масти больше или равно 5 $result = straightFlush($suit3); if(0 == $result) { foreach ($suit3 as $key3 => $s3) { $suit3[$key3] = $s3 % 100; } rsort($suit3); $result = $suit3[0] * 1e+10 + $suit3[1] * 1e+8 + $suit3[2] * 1e+6 + $suit3[3] * 1e+4 + $suit3[4] * 1e+2; } } else if (count($suit4) >= 5) { //если количество карт четвертой масти больше или равно 5 $result = straightFlush($suit4); if(0 == $result) { foreach ($suit4 as $key4 => $s4) { $suit4[$key4] = $s4 % 100; } rsort($suit4); $result = $suit4[0] * 1e+10 + $suit4[1] * 1e+8 + $suit4[2] * 1e+6 + $suit4[3] * 1e+4 + $suit4[4] * 1e+2; } } else { $result = 0; } return $result; }
Функция для определения Стрит Флеша (аналогична Стриту, только нужно учесть, что количество элементов массива может быть не только 7, но еще и 5, и 6, и что возвращаемый результат этой функции должен быть больше):
function straightFlush(array $arrayCards) { $newArrayCards = []; $ace = false; foreach ($arrayCards as $arrayCard) { $newArrayCards = array_merge($newArrayCards, [$arrayCard % 100]); if (14 == $arrayCard % 100) { $ace = true; } } if ($ace == true) { $newArrayCards = array_merge($newArrayCards, [1]); } rsort($newArrayCards); $count = 0; $length = 4; $result = 0; $begin = 0; for ($i = 1; $i <= $length; $i++) { if (-1 == ($newArrayCards[$i] - $newArrayCards[$i - 1])) { $count++; } if ($length == $i) { if (4 == $count) { $result = $newArrayCards[$begin] * 1e+16; } else if ((7 == count($arrayCards) and ($length < 6 or (6 == $length and $ace == true))) or (6 == count($arrayCards) and ($length < 5 or (5 == $length and $ace == true))) or //если число элементов исходного массива = 6 (5 == count($arrayCards) and (5 == $length and $ace == true))) { //если число элементов исходного массива = 5 $length++; $begin++; $i = $begin; $count = 0; } } } return $result; }
Определение и расчет парных комбинаций
Существует пять парных комбинаций: каре (4 карты одного достоинства), фулл хаус (3 карты одного достоинства и 2 карты другого достоинства), тройка (3 карты одного достоинства), две пары (2 карты одного достоинства и 2 карты другого достоинства), пара (2 карты одного достоинства).
Важно при расчете помнить, что в комбинации фулл хаус главным является сочетание из трех карт, а из двух второстепенным, т.е., например, комбинацию из 3-х двоек и 2-х десяток будем считать, как 3*1e+12 + 10*1e+10, а не по достоинству карт.
А также при расчете парных комбинаций нужно учитывать проблемы:
1. То, что более 5 карт образуют комбинацию.
2. То, что у нас может возникнуть ситуация, при которой комбинаций более одной.
Для подсчета совпадения карт буду использовать три счетчика, которые будут считать парные карты в колоде. Там, где счетчик равен 1 — участвуют 2 карты в комбинации, где счетчик 2 — 3 карты, где 3 — 4 карты.
Рассмотрим все возможные варианты сочетания счетчиков:
100 — пара
110 — 2 пары
111 — 3 пары (нужно преобразовать к виду 110 (две пары))
200 — тройка
210 — фулл хаус
120 — фулл хаус (нужно преобразовать к виду 210 (фулл хаус))
211 (121, 112) — фулл хаус + пара (нужно преобразовать к виду 210 (фулл хаус))
220 — 2 тройки или фулл хаус + 1 карта (нужно преобразовать к виду 210 (фулл хаус))
300 — каре
310 (130) — каре + пара (нужно преобразовать к виду 300 (каре))
320 (230) — каре + тройка (нужно преобразовать к виду 300 (каре))
Далее произведу окончательный расчет парных комбинаций.
function couple(array $arrayCards) { $newArrayCards = []; foreach ($arrayCards as $arrayCard) { $newArrayCards = array_merge($newArrayCards, [$arrayCard % 100]); //создаю массив с остатками от деления на 100 } rsort($newArrayCards); //сортирую массив с числами в порядке убывания $count1 = 0; //счетчик для первой пары $count2 = 0; //счетчик для второй пары $count3 = 0; //счетчик для третьей пары $match1 = 0; //сюда положу значение первой пары $match2 = 0; //сюда положу значение второй пары $match3 = 0; //сюда положу значение третьей пары for($i = 1; $i < count($newArrayCards); $i++) { if ($newArrayCards[$i] == $match1 or $match1 == 0) { //первое парное сочетание if ($newArrayCards[$i] == $newArrayCards[$i - 1]) { $match1 = $newArrayCards[$i]; $count1++; } } else if ($newArrayCards[$i] == $match2 or $match2 == 0) { //второе парное сочетание if ($newArrayCards[$i] == $newArrayCards[$i - 1]) { $match2 = $newArrayCards[$i]; $count2++; } } else if ($newArrayCards[$i] == $match3 or $match3 == 0) { //третье парное сочетание if ($newArrayCards[$i] == $newArrayCards[$i - 1]) { $match3 = $newArrayCards[$i]; $count3++; } } } //здесь я преобразую 111 к 110 (2 пары) и 211 к 210 (фулл хаус) if(($count1 == 1 or $count1 == 2) and $count2 == 1 and $count3 == 1) { $count3 = 0; } //здесь я преобразую 121 сначала к 211 для простоты вычислений, а затем к 210 (фулл хаус) else if($count2 == 2 and $count1 == 1 and $count3 == 1) { $support = $match2; $match2 = $match1; $match1 = $support; $count1 = 2; $count2 = 1; $count3 = 0; } //здесь я преобразую 112 сначала к 211 для простоты вычислений, а затем к 210 (фулл хаус) else if($count3 == 2 and $count1 == 1 and $count2 == 1) { $support = $match3; $match2 = $match1; $match1 = $support; $count1 = 2; $count3 = 0; } //здесь я преобразую 220 к 210 (фулл хаус) else if($count1 == 2 and $count2 == 2 and $count3 == 0) { $count2 = 1; } //здесь я преобразую 120 к 210 (фулл хаус) else if ($count1 == 1 and $count2 == 2 and $count3 == 0) { $support = $match1; $match1 = $match2; $match2 = $support; $count1 = 2; $count2 = 1; } //320 к 300 и 310 к 300 else if($count1 == 3 and ($count2 == 2 or $count2 == 1)) { $count2 = 0; } //230 к 320 и затем к 300 и 130 к 310 и затем к 300 else if($count2 == 3 and($count1 == 2 or $count1 == 1)) { $support = $match2; $match2 = $match1; $match1 = $support; $count1 = 3; $count2 = 0; } //каре if ($count1 == 3) { $count1 = 1e+14; } //фулл хаус else if ($count1 == 2 and $count2 == 1) { $count1 = 1e+12; $count2 = 1e+10; } //тройка else if ($count1 == 2 and $count2 == 0) { $count1 = 1e+6; } //2 пары else if ($count1 == 1 and $count2 == 1) { $count1 = 1e+4; $count2 = 1e+2; } //пара else if ($count1 == 1 and $count2 == 0) { $count1 = 1e+2; } $result = $match1 * $count1 + $match2 * $count2;// $match1 и $match2 будут равны 0, если совпадений не было return $result; }
Окончательный расчет
Теперь у нас имеются функции, которые проверяют массив карт на стрит, флеш, стрит флеш и парные комбинации. Вызову их вместе и посчитаю окончательный результат, не забываю также и про расчет старшей карты.
Расчет старшей карты:
1. Определю наибольшую и наименьшие по приоритетности карманные карты. Для этого сравню их остатками от деления на 100 (%100).
2. К наибольшей по прио��итетности карте добавлю наименьшую по приоритетности деленную на 100.
function priority(array $arrayCards) { //здесь я определяю старшую карту if($arrayCards[5] % 100 > $arrayCards[6] % 100) { //условились, что две последние карты массива - карманные $highCard1 = $arrayCards[5] % 100; $highCard2 = $arrayCards[6] % 100; } else { $highCard1 = $arrayCards[6] % 100; $highCard2 = $arrayCards[5] % 100; } $flush = pokerFlush($arrayCards); //вызываю функцию для расчета флеша $straight = straight($arrayCards); //вызываю функцию для расчета стрита $couple = couple($arrayCards); //вызываю функцию для расчета пар //далее определяю результат согласно приоритету комбинаций if($flush >= 1e+16) { $result = $flush; //стрит флеш } else if($couple >= 1e+14 and $couple < 1e+16) { $result = $couple; //каре } else if($couple >= 1e+12 and $couple < 1e+14) { $result = $couple; //фулл хаус } else if($flush >= 1e+10) { $result = $flush; //флеш } else if($straight >= 1e+8 and $straight < 1e+10) { $result = $straight; //стрит } else if($couple >= 1e+6 and $couple < 1e+8) { $result = $couple; //тройка } else if($couple >= 1e+4 and $couple < 1e+6) { $result = $couple; //две пары } else if($couple >= 1e+2 and $couple < 1e+4) { $result = $couple; //пара } else { $result = $highCard1 + $highCard2 * 1e-2; //старшая карта } return $result; }
О выводе на экран
Выводила я результат в браузер, используя вот такой спрайт (брала картинку с сайта: fondhristianin.ru/?p=2941), как background-image для div.

Ширину и высоту для div задавала равными размеру одной карты, т.е. если размер изображения 572px*328px (как в данном случае), а в ширину количество карт равно 13, в высоту — 5, ширину и высоту задавала 44px*65.6px. Далее меняла background-position с учетом ранее сформированного массива карта.
Расчет background-position для любого из divов по оси х:
100/12 * ($arrayCards[$i] % 100 - 2)
где $arrayCards — ранее сформированный массив.
$i — порядковый номер карты.
Пояснения к расчету:
В ряду 13 карт, начало 1-ой карты — 0%, начало 13-ой карты — 100%, поэтому разница позиций — 100% / 12 (а не 13). Приводим числа карт из вида [2-14, 102-114, 202-214, 302-314] к [0-12]. Для этого возьмет остаток от числа карты и отнимем от остатка 2. Умножим полученное число на разницу позиций.
Расчет background-position для любого из divов по оси y:
100/4 * floor($arrayCards[$i] / 100)
Пояснение к расчету:
В ряду 5 карт, начало 1-ой карты — 0%, начало 5-ой карты — 100%, поэтому разница позиций — 100% / 4 (а не 5). Приводим числа карт из вида [2-14, 102-114, 202-214, 302-314] к [0-3]. Для этого разделим число карты на 100 и округлим в меньшую сторону (можно использовать и простую операцию округления, она сработает аналогичным образом). Умножим полученное число на разницу позиций.