Видеопокер

Исходный файл: Videopoker.fla

В казино автоматы с видеопокером располагаются рядом с игровыми автоматами. По существу, они представляют собой то же самое: вы вставляете монету и можете выиграть некоторую сумму денег. Единственное различие состоит в том, что в течение игры вы должны совершать некоторые действия, чтобы повлиять на результат, а автомат будет на них реагировать.
Не ошибитесь: казино всегда выигрывает. Но так как игрок может заменять некоторые карты в течение игры, ему кажется, что у него появляется больше шансов на выигрыш. Такая возможность усложняет написание программы.

Задача проекта

Эта игра состоит из трех шагов. Первый шаг - игра ждет, пока игрок попросит сдать карты. На втором шаге игрок смотрит на пять карт, которые ему раздали, и решает, какие оставить, а какие - поменять. И последний шаг - раздаются новые карты, и подсчитывается сумма выигрыша. После чего осуществляется переход к первому шагу.
Игра должна представить игроку набор из пяти карт, выбранных случайным образом из перемешанной колоды (рис. 15.3).

Рисунок 15.3 Видеопокер показывает игроку пять карт


Затем игрок может выбрать те карты, которые следует поменять (можно поменять все). После замены программа должна рассмотреть окончательный набор карт и подсчитать, сколько они "стоят".

Подход

Первое, что необходимо программе, - это перетасованная колода карт, которая будет представлять собой массив с символьными данными, например "h7", что означает семерка червей. Четыре масти представлены буквами "с", "d", "h" и "s". Туз обозначен "1", то есть «c1" означает туз треф, а валет, дама и король обозначены цифрами "И", "12" и "13" соответственно.
Создать упорядоченную колоду карт просто, совсем другое дело - создание перетасованной колоды. Для этого вы берете упорядоченный массив и, выбирая из него случайным образом элементы, один за другим помешаете их в новый массив.
Затем первые пять карт в массиве отображаются на экране. Под каждой картой располагается кнопка. Один щелчок мыши по ней переворачивает карту, так что будет видна рубашка карты. Второй щелчок возвращает карту в исходное положение на тот случай, если игрок передумал.
Когда игрок будет удовлетворен своим выбором, он щелкает по кнопке Draw (Поменять). Те карты, которые были выбраны длят замены, замешаются следующими картами из перетасованной колоды.
Самый сложный фрагмент программного кода нужен для конечной фазы игры. Окончательный массив из пяти карт должен быль оценен с точки зрения покера. Вот список возможных вариантов:
младшая пара - пара карт одного достоинства (десятка или ниже);
старшая пара - пара валетов, дам, королей или тузов;
тройка - три карты одного достоинства;
стрит - пять карт, ранг каждой из которых на единицу превышает ранг предыдущей карты, например восьмерка, девятка, десятка, валет и дама. Туз может располагаться как перед двойкой, так и послекороля;
флэш - пять карт одной масти;
фул хаус - пара и три карты одного достоинства;
каре - четыре карты одного достоинства;
стрейт флэш - пять карт одной масти, ранг каждой из которых на единицу превышает ранг предыдущей карт;
роял флэш - стрейт флэш с десяткой, валетом, дамой, королем и тузом.
Для того чтобы определить, удовлетворяет ли набор карт вышеуказанным критериям, необходимо проанализировать массив с картами несколькими способами.
После того как будет определена ценность карт, останется сделать последний шаг - сопоставить ее с суммой выигрыша и увеличить наличность игрока.

Подготовка ролика

Основной библиотечный элемент в данной игре - колода карт, то есть клип с 54 кадрами. Первый кадр отображает пустой контур карты. Во втором кадре содержится рубашка карты. Кадры 3-54 показывают различные карты колоды. Каждый кадр имеет такое же имя, какое используется в коде для идентификации карт. Например, "c1" - туз треф и "h11" -валет червей.
Просмотрите ролик Videopoker.fla и вы увидите, что для клипа "deck" выделена целая папка библиотеки, заполненная графическими изображениями. Это упрощает повторное использование элементов для разных карт.
Ролик составлен из пяти экземпляров клипа "deck" (колода), которые называются "card0", "card1", "card2", "card3" и "card4". В первом кадре
нужна только кнопка "Deal" (Раздать), которая отображается также и в третьем кадре.
Во втором кадре находится кнопка "Draw" (Выигрыш), а под каждой картой - кнопка "Hold/Replace" (Оставить/Поменять).

Создание кода

Большая часть кода содержится в первом кадре основной временной шкалы. Начинается она с того, что игроку предоставляется 100 долларов.

startGame();
stop () ;
// "Выдаем" исходную сумму,
function startGame() {
cash = 100;
}

Как и в предыдущем проекте, перед суммой наличных денег игрока отобразите знак "$".

// Отображаем сумму наличных со знаком доллара,
function showCash() {
cashDisplay = "$"+cash;
}

Раздача карт начинается с того, что у игрока изымается один доллар. Каждая раздача производится из новой колоды, состоящей из 52 карт. Функция f irstDraw берет первые пять карт, а функция showCards помешает клипы соответствующих карт на рабочее поле.

// Сдача карты,
function startDeal() {
// Уменьшаем сумму наличных денег.
cash--;
showCash();
// Перетасовываем карты и снова сдаем их.
createDeck();
f irstDraw();
showCards(); }

Создание полностью произвольной перетасованной колоды включает в себя два шага. Первый - создание упорядоченной колоды. Это осуществляется путем циклического просмотра всех мастей и всех рангов карт и для каждой комбинации добавляется соответствующий элемент массива.
Затем программа случайным образом выбирает карты из упорядоченной колоды и помещает их в другой массив. Когда массив заполняется, а предыдущий массив оказывается пустым, у вас получается перетасованная колода карт.

// Создаем перетасованную колоду,
function createDeckO {
// Создаем упорядоченную колоду,
suits = ["с","d","s","h"];
temp = new Array();
for(suit=0; suit<4; suit++) {
for (num=l; num<14; num++) {
temp.push(suits[suit]+num);
}
// Случайным образом выбираем карты,
// пока колода не будет полностью перетасована.
deck = new Array();
while (temp.length > 0) {
r = int(Math.random()*temp.length),
deck.push(temp[r]);
temp.splice(r,1); }

Функция firstDraw берет пять карт из колоды и помешает их в массив cards, а также создает небольшой массив hold, в котором хранятся имена тех карт, которые игрок хочет оставить.

// Сдаем первые пять карт.
function firstDraw() {
cards = new Array();
for (i=0; i<5; i++) {
cards.push(deck.pop());
}
// Допускаем, что игрок оставляет все карты,
hold = [true, true, true, true, true];
showCards();
}

Для того чтобы преобразовать содержимое массива cards в то, что игрок видит на экране, функция showCards на рабочем поле устанавливает кадры для каждого из пяти экземпляров клипа. Кадры должны соответствовать символьной строке, расположенной в массиве hand.

// Определяем вид клипов карт, сданных игроку. function showCards() {
for (i=0; i<5; i++) {
_root["card"+i].gotoAndStop(cards[i]) ;
}}

После того как все карты будут показаны игроку, он должен решить, что делать дальше. Кнопка "Hold/Draw" под каждой картой вызывает функцию holdDraw и передает ей число от 0 до 4.
Первый раз, когда щелкают по кнопке, программа изменяет экземпляр клипа так, что отображается рубашка карты. Если игрок щелкает по ней еще раз, карта возвращается в исходное состояние. Игрок может сколько угодно переворачивать карты, прежде чем щелкнуть по кнопке Draw.
В массиве hold будет находиться значение true, если игрок хочет оставить соответствующую карту, и false, если хочет ее заменить.

// Переворачиваем карту, предназначенную для замены,
function holdDraw(cardNum) {
// Переворачиваем карту, которая находится среди тех,
// которые игрок хочет оставить,
if (hold[cardNum]) {
_root["card"+cardNum].gotoAndStop("back");
hold[cardNum] = false;
// Если карта перевернута еще раз, оставляем ее.
} else {
_root["card"+cardNum].gotoAndStop(cards[cardNum]);
hold[cardNum] = true;
}}

Когда игрок щелкает по кнопке "Draw", функция secondDraw заменяет те карты, для которых в массиве hold значения были равны false. Затем вызывается функция showCards, чтобы изменения были отражены на экране.
Затем программа с помощью функции handvalue определяет, какой расклад имеется у игрока. Ценность расклада передается функции winning, которая рассчитывает, какую сумму следует добавить к величине cash (сумме наличных). Переменная resultsDisplay используется для отображения этих значений на экране.

// Заменяем карты и определяем выигрыш,
function secondDraw() {
// Заменяем карты, for (i=0; i<5; i++) {
if (!hold[i]> { cards[i] = deck.pop();
showCards();
// Определяем, что на руках у игрока.
handVal = handValue(cards);
// Расчитываем сумму выигрыша.
winAmt = winning(handVal);
resultsDisplay = handVal + ": " + winAmt;
// Добавляем сумму выигрыша к имеющейся сумме наличных,
cash += winAmt;
showCash();
gotoAndPlay("done");
}

Прежде чем перейти к рассмотрению функции handvalue, необходимо создать функцию compareHands. Функция handvalue сортирует карты на руках у игрока по возрастанию. Программа Flash ничего не знает о колоде игральных карт, так что вам придется "научить" ее распознавать сочетания покера.
Функция compareHands берет две карты и сравнивает их. Для каждой карты из символьной строки она выбирает первый и второй символы, то есть игнорирует нулевой символ. Таким образом, карта "с7" становится "7", а "с13" - "13".
Затем функция возвращает один из трех вариантов ответов: -1 - первая карта меньше по достоинству второй карты, 0 - карты одинакового достоинства, и 1 - ранг первой карты на единицу больше ранга второй.
Эта функция необходима для команды sort, использующейся в функции handvalue. Если для сортировки не будет специальной функции, программа попытается отсортировать массив hand по алфавиту, то есть все трефы будут расположены перед бубнами, так как трефовая масть начинается с буквы "с", а бубновая - с "d". А вам нужно, чтобы карты были отсортированы в соответствии с их рангом.

// Эта функция используется командой сортировки для определения,
// какие карты идут первыми,
function compareHands(a,b) {
// Получаем ранг карты.
numa = Number(a.substr(1,2));
numb = Number (b. subs t r (1,2) ) ;
// Возвращаем -1, 0 или 1 в зависимости
//от результата сравнения,
if (numa < numb) return(-1);
if (numa == numb) return(O);
if (numa > numb) return(1);}

Следующая функция handvalue начинается с того, что копирует массив cards и помешает его в массив hand. Затем она сортирует полученный массив с использованием функции compareHands (1).
Например, если на руках у игрока имеются карты ["h4", "d5", "c2", "s3", "h6"], после сортировки массив будет выглядеть следующим образом: ["с2", "s3", "h4", "d5", "h6"]. Так гораздо проще узнать, находится ли на руках у игрока "стрит".
"Стрит" определяется путем просмотра каждой карты и выяснением, больше ли ранг этой карты на единицу ранга карты предыдущей (2). Если такое условие выполняется для всего массива, то тогда на руках у игрока "стрит".
Таким образом будет определен не только "стрит": когда "стрит" начинается с десятки и заканчивается тузом, то это "флэш стрит". Произошло ли так или нет, можно определить с помощью простого теста (3).
Затем вы проверяете, одной ли масти карты (4). Для этого все карты, кроме первой, сравниваются с первой. Если масть всех карт совпадает с мастью первой карты, значит, все карты одной масти.
На следующем шаге создается массив counts, в котором будет храниться число карт одинакового достоинства (5). В этом массиве находится 14 элементов, каждый из которых будет отвечать за число карт определенного ранга, имеющихся у ифока. Первый элемент массива не используется, так как нет карт с нулевым рангом.

Некоторые люди думают, что стритом можно считать и такой набор карт, в котором задействован туз. Например, дама, король, туз, двойка и тройка. Это не стрит, а всего лишь его дополнительная вариация, используемая в любительских играх в покер. Туз может быть использован и как первая карта стрита (туз, двойка, тройка, четверка, пятерка), и как последняя (десять, валет, дама, король, туз), но только не в середине.

Например, если на руках туз, две тройки, четверка и валет, массив будет выглялеть следующим образом: [0,1,0,2,1,0,0,0,0,0,0,1,0,0].
Теперь, наконец, программа может начать определение расклада на руках у игрока. Просматривается массив counts и отмечаются все значения пары, тройки или четверки (6). Если один раз встречаются две карты одного достоинства, то у игрока пара. Если один раз встречается три карты одного достоинства, то у игрока тройка; если один раз встречается четыре карты одного достоинства, то - каре. Также можно дважды обнаружить две карты одного достоинства или пару и тройку. В первом случае будет две пары, а втором - фул хаус.
Затем проверяется, есть ли на руках пары с валетом или картами высшего достоинства (7). Обычно видеопокер отмечает только такие пары.
Следующий тест проверяет, есть ли в раскладе туз (8). Это будет нужно для определения флэш стрита. Если у ифока флэш стрит, и одна из карт - туз, то у него самый высший тип флэш стрита - флэш роял.
Теперь у функции есть весь набор значений логических переменных: straight, flush, hasAce, fourOfAKind, threeOfAKind, twoPair, pair, fullHouse и jackOrHigher. С их помощью определяется действительная ценность расклада, и возвращается символьная строка (9).

// Определяем расклад,
function handValue() {
// Копируем и сортируем карты игрока.
hand = cards.slice();
(1) hand, sort (compareHands) ;
// Создаем массив мастей и рангов для более легкого доступа
//к ним.
suits = new Array();
nums = new Array();
for (i=0; i<5; i++) {
suits.push(hand[i].substr(0,1));
nums.push(Number(hand[i].substr(1,2)));
(2)// Проверяем, расположены ли они по возрастанию,
straight = true;
for (i=0; i<4; i++) {
if (nums[i]+l != num[i+l]) straight = false;
(3)// Ищем десятку, валета, даму, короля и туза,
if (nums[0] == 1) and (nums[l] == 10) and (nums[2]) == 11)
and (nums[3] == 12) and (nums[4] == 13)) straight = true;
(4)// Выясняем, одной ли масти карты,
flush = true;
for (i=l; i<5;l i++) {
if (suits[i] != suitstO]) flush = false;
// Создаем массив для подсчета, сколько карт одного
(5)// достоинства находится в раскладе,
counts = new Array ();
for (i=0; i<14; counts.push(O) ;
for (i=0; i<5;i++) counts[nums[i]]+ +;
(6)// Используя массив counts, ищем совпадения,
pair = false;
twoPair = false;
threeOfAKind = false;
fourOfAKind = false;
for (i=l; i<14; i++) {
// Найдена пара.
if (counts[i] == 2) {
// Найдена вторая пара.
if (pair) {
twoPair = true;
// Найдена только одна пара.
} else {
pair = true;
// Три карты одного достоинства.
} else if (counts[i] == 3) {
threeOfAKind .= true;
// Четыре карты одного достоинства.
} else if (countsfi] == 4) {
fourOfAKind = true;
// Проверяем, есть ли среди совпадающих карт валеты или
(7)// карты более высокого ранга.
jackOrHigher = false;
for (i=l; i<14; i++) {
if (((i==l) or (i>10) and (counts[i]>=2)) {
jackOtHigher = true;
(8) // Выясняем, является ли карта более высокого ранга тузом.
hasAce = false; if (counts[1] > 0) hasAce = true;
(9)// Делаем вывод, какой расклад на руках у игрока.
if (straight and flush and hasAce) {
return("Royal Flush");
} else if (straight and flush) {
return("Straight Flush");
} else if (fourOfAKind) {
return("Four-Of-A-Kind");
} else if (pair and threeOfAKind) {
return ("Full House");
} else if (flush) {
return("Flush");
} else if (straight) {
return("Straight");
} else if (threeOfAKind) {
return("Three-Of-A-Kind");
} else if (twoPair) {
return("Two Pair");
} else if (pair and jaskOrHigher) {
return!"High Pair"); } else if (pair) {
return("Low Pair");
} else {
return("Nothing");

И последняя оставшаяся функция - winning, которой передается строчка, сгенерированная функцией handvalue и которая определяет соответствующую сумму выигрыша.

//Исходя из типа расклада возвращаем сумму выиграша
function winnings(handVal) {
if (handVal == "Royal Flush") return(800);
if (handVal == "Straight Flush") return(50);
if (handVal == "Four-Of-A-Kind") return(25);
if (handVal == "Full House") return(8);
if (handVal == "Flush") return(5);
if (handVal == "Straight") return(4);
if (handVal == "Three-Of-A-Kind") return(3);
if (handVal == "Two Pair") return(2);
if (handVal == "High Pair") return(1);
if (handVal == "Low Pair") return(0);
if (handVal == "Nothing") return(0);
}

К сведению

К каждой кнопке Hold/Draw прикреплен собственный фрагмент кода. Для первой кнопки он выглядит следующим образом:

on (press) { holdDraw(0);}

Цифра 0 сообщает функции holdDraw, что действие производится с нулевой картой, для остальных четырех кнопок вместо нуля стоят числа от 1 до 4.
Хотя первый кадр ролика сразу же вызывает функцию startGame (см. раздел "Создание кода"), второй кадр должен вызывать функцию startDeal. Просмотрите ролик, приведенный в качестве примера, чтобы самостоятельно изучить, как это реализовано.

Другие возможности

Суммы, указанные в функции winnings, я выбрал, руководствуясь обычными правилами покера. Однако вы можете указать другие числа в зависимости от той суммы, которую хотите переводить на счет виртуального казино или игрока.
Можно сделать и так: когда на счету игрока не остается денег, переходить, например, к кадру game over.
Еше одна вариация игры - позволить игроку ставить от одного до пяти долларов за игру. Таким образом, игрок ставит меньшую сумму, когда чувствует, что ему не везет, и большую - в других случаях.