137_Optimized - Стр 10 (2024)

137_Optimized - Стр 10 (1)

hang

e

C

E

X

-

d

F

t

D

i

r

P

NOW!

o

BUY

to

w Click

m

w

w

o

.

.c

p

g

df

n

e

-xcha

hang

e

C

E

X

-

d

F

t

D

i

r

P

NOW!

o

BUY

to

w Click

m

w

w

o

.

.c

p

g

df

n

e

-x cha

ПрограммаtangoGPS

GPSDrive собственнойперсоной

можнымвоспроизведениефильмовимузыки. Какобычно, вштатной поставкеUbuntu кодековнет, поэтомуподключимрепоMedibuntu для установкивсегонеобходимого. Откройтерминалинаберикоманды:

$ sudo wget --output-document=/etc/apt/sources.list.d/ medibuntu.list http://www.medibuntu.org/sources. list.d/$(lsb_release -cs).list

$ sudo apt-get --quiet update

$ sudo apt-get --yes --quiet --allow-unauthenticated install medibuntu-keyring

$ sudo apt-get --quiet update

Послеэтоговведикомандудляустановкикодеков:

$ sudo apt-get install w32codecs

ТеперьинсталлируемMPlayer иоболочкудлянего:

$ sudo apt-get install mplayer non-free-codecs libdvdcss2 smplayer

Пакеты«посередине» — этодополнительныекодекиибиблиотека DVDCSS2, позволяющаячитатьлицензионныеDVD-диски. Теперьприступимквеб-камере. Linux хорошоподдерживаеттакие

гаджеты, нужнотольковыбратьпрограммудляработы. Здесьдефицитатоженет. Вотнеполныйпереченьпрограммдляработыскамерой: cheese (самаяпростая), webcam (невсегдапочему-тоработает), camorama, camstream идр. Выбирайту, котораябольшевсегонравитсятебе.

Следующийшаг— раскочегаритьUSB-модем. Однакопроцедура настройкибудетразнойнетолькодляразныхоператоров, ноидля разныхмодемов. ЧтобынастроитьсоединениечерезNetworkManager (у меняМТС-Коннект), пришлосьнемногопоковыряться. Забегаявперед, скажу, чтовсеописанноенижеможнобылореализоватьгораздокрасивее— черезпрограммуusb_modeswitch, нояпошелподругомупути. Он проверениработает, поэтомуегоиопишувстатье. Когдатыподключаешьмодемксистеме, онсначалаопределяетсякакобычнаяфлешка. Надозайтинанегоископироватьодинфайлв/usr/local/bin:

$ sudo cp /media/CNU-680/Linux/RDEVCHG /usr/local/bin

Послеэтогооткрыть/etc/sudoers:

$ sudo gedit /etc/sudoers

Идобавитьвнегоследующуюстроку:

XÀÊÅÐ 06 /137/ 10

%admin ALL=NOPASSWD: /usr/local/bin/RDEVCHG

ЭтиммыразрешаемзапускпрограммыRDEVCHG беззапросапароля. Послеэтогонужнообеспечить, чтобымодемопределялсякакмодем, а

некакфлешка. Дляэтогооткройфайл/etc/udev/rules.d/70-persistent-cd. rules, найдифрагменттекста:

ENV{ID_CDROM}=="?*", ENV{ID_SERIAL}=="CMOTECH_

Mass_Storage_000000000002-0:0", SYMLINK+="cdrom1",

ENV{GENERATED}="1" ENV{ID_CDROM}=="?*", ENV{ID_

SERIAL}=="CMOTECH_Mass_Storage_000000000002-0:0",

SYMLINK+="dvd1", ENV{GENERATED}="1"

изамениегона:

ENV{ID_CDROM}=="?*", ENV{ID_SERIAL}=="CMOTECH_ Mass_Storage_000000000002-0:0", SYMLINK+="cdrom1", ENV{GENERATED}="1" RUN+="/usr/bin/sudo /usr/ local/bin/RDEVCHG" ENV{ID_CDROM}=="?*", ENV{ID_ SERIAL}=="CMOTECH_Mass_Storage_000000000002-0:0", SYMLINK+="dvd1", ENV{GENERATED}="1" RUN+="/usr/bin/ sudo /usr/local/bin/RDEVCHG"

ТеперьвNetworkManager правим«Автоматическоесоединение CDMA». Всепараметрыоставляемпоумолчанию, кромеименипользователя(mts) ипароля(internet). Поаналогиисвиндойможноперезапус- титьсистему—так, навсякийслучай. Послеэтоговседолжнозаработать какположено.

ДлямодемовAny DATA ADU-500A (тожедовольнораспространены) мож-

ноиспользоватьпрограммуMTS Connect (mtsconnect.sourceforge.net).

ЧтокасаетсяGPS: Linux поддерживаетGPS-приемники, нолучше использоватьвнешние, аневстроенныевустройства, называемые автомобильнымиGPS-навигаторами. Еслиутебяестьавтомобильный GPS-навигатор, нетникакихгарантий, чтотызаставишьегоработать подLinux.

СамыеудачныеGPS-программыдляLinux: Navit, tangoGPS иGPSDrive. Navit толькопоказываеттекущееположениеавтомобиля, аостальные двемогутещеизаписыватьмаршруты. Ниоднапрограмманеумеет «разговаривать». АвотдажесамыедешевыекитайскиеGPS-навигато- рыговоритьумеют, даещеипо-русски. Всетрипрогиможноустановить изрепоUbuntu, такчтосинсталляциейпроблемвозникнутьнедолжно. Navit используеткартынавитела, tangoGPS иGPSDrive —OpenStreetMap (www.openstreetmap.org). GPSDrive загружаеткартыналету—есликар- татекущейместностиещенезалита, нужновыполнитькомандуOptions

ÆMaps ÆDownload.

Вотвродебыивсе. Осталосьвоплотитьописанноевжизнь!z

137_Optimized - Стр 10 (2) 089

137_Optimized - Стр 10 (3)

hang

e

C

E

X

-

d

F

t

D

i

r

P

NOW!

o

BUY

w Click

to

CODING

m

Аверин Евгений timreset@mail.ru, javatalks.ru

w

w

o

.

.c

p

g

df

n

e

-xcha

hang

e

C

E

X

-

d

F

t

D

i

r

P

NOW!

o

BUY

to

w Click

m

w

w

o

.

.c

p

g

df

n

e

-x cha

НАТЯГИВАЕМ

СЕТЕВЫЕ

POKER

ROOM’Û

КОДИНГ ПОКЕР-БОТА:

ЛОГИКА ПРИНЯТИЯ РЕШЕНИЙ

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

090

XÀÊÅÐ 06 /137/ 10

137_Optimized - Стр 10 (4)

hang

e

C

E

X

-

d

F

t

D

i

r

P

NOW!

o

BUY

to

w Click

m

w

w

o

.

.c

p

g

df

n

e

-xcha

hang

e

C

E

X

-

d

F

t

D

i

r

P

NOW!

o

BUY

to

w Click

m

w

w

o

.

.c

p

g

df

n

e

-x cha

DVD

NEGOTIATOR

dvd

Надискележат

исходникикласса,

реализующегорасчет

вероятностивыигры-

ша, unit-тестыкнему,

исходниккалькулято-

ра, парастатей

опокер-ботах

LOGIC

STATISTICS

исписокинтересных

ссылок.

Простая схема покер-бота

ЧТО ЕСТЬ ПОКЕР?

В отличие от шахмат, покер — игра с неполной информацией. То есть, игроки не знают, какая карта есть на руках у оппонентов — они могут это лишь предполагать с определенной степенью вероятности.

Правила покера просты — выигрывает тот, у кого на руках сильнейшая комбинация, составленная из его карт и тех, что на столе, или последний оставшийся игрок, если все остальные сбросили. Всего в покере десять комбинаций: Роял-флеш, Стрит-флеш, Каре, Фулл-хаус, Флеш, Стрит, Сет/Трипс/Тройка, Две пары, Одна пара, Старшая карта. Существует много разных видов покера. Здесь мы будем рассматривать Texas Holdem No Limit Poker, он же тур-

нирный покер.

ПРОСТАЯ СХЕМА ПОКЕР-БОТА

Из каких блоков будет состоять наш робот? Рассмотрим по пунктам:

Logic — Блок логики принятия решений (Fold, Call, Raise) Negotiator — Блок взаимодействия с программой для игры в покер;

Statistics — Блок обработки и накопления статистики по игрокам.

Из Negotiator в Logic поступает информация о текущих действиях на столе. Из Logic в Negotiator — информация о своих действиях, которые нужно совершить (сделать

Fold, Call или Raise). Из Negotiator в Statistics — инфор-

мация о действиях игрока за столом для последующей обработки и хранения. Из Statistics в Logic — информация о статистических данных игроков.

ПРИНЯТИЕ РЕШЕНИЙ В ПОКЕРЕ

Как правило, во время каждого хода игрок может при-

нять три решения: Fold, Call, Raise. Есть еще All-in

— когда денег для продолжения игры нет, и придется поставить все. Существует множество алгоритмов принятия решений: DIVAT анализ, дерево решений, различные эмпирические алгоритмы (кстати, большинство из этих алгоритмов используют вероятность выигрыша). Мы будем использовать в принятии решений беспристрастную математику, а точнее — теорию вероятности.

ПРИМЕЧАНИЕ О ТЕОРИИ ВЕРОЯТНОСТИ

В реальной жизни она применяется в основном для расчета отказоустойчивости механизмов, но ее можно очень хорошо применять также в играх с неполной информацией и большим количеством раундов: рулетка, покер, блэкджек и др. В теории вероятности есть несколько парадоксов, которые не соотносятся с нашим жизненным опытом, но, тем не менее, являются правдой. Это, например, парадоксы Монти-Холла и Паррондо.

МАТЕМАТИЧЕСКАЯ СТОРОНА ПРИНЯТИЯ РЕШЕНИЙ В ПОКЕРЕ

Формула принятия решений в покере чрезвычайно проста:

p*pot = win, где p — вероятность выигрыша с текущими картами (на руках и на столе), pot

— размер банка на момент принятия решения, win — выигрыш, который мы получим, если будем разыгрывать множество партий с этими картами.

Åñëè win < bet_cur, òî Fold

О ЗАКОННОСТИ СОЗДАНИЯ ПОКЕР-БОТОВ

По законам РФ и других государств создание и использование покер-ботов (и ботов для других игр) не запрещено. А по правилам всех известных мне покер-румов — запрещено. Что это значит? Если ты попался на использовании бота, то самое страшное, что тебе грозит — это бан аккаунта и списание с него всех средств. Все. Никаких штрафов, повесток в суд, блокировки кредитки и т.д. не последует. Кстати, практически во всех покер-румах разрешается использование «помощников» в игре — различных калькуляторов и программ для сбора статистики.

137_Optimized - Стр 10 (5) HTTP://WWW

137_Optimized - Стр 10 (6) links

Рекомендуюпочитать ввикистатьиопокере, теориивероятностииматематическом ожидании, посетить pokerai.org — лучший сайтопокерномAI, а такжепочитатьцикл статейосоздании покер-ботанаwww. codingthewheel.com

XÀÊÅÐ 06 /137/ 10

091

137_Optimized - Стр 10 (7)

hang

e

C

E

X

-

d

F

t

D

i

r

P

NOW!

o

BUY

to

w Click

m

w

w

o

.

.c

p

g

df

n

e

-xcha

CODING

hang

e

C

E

X

-

d

F

t

D

i

r

P

NOW!

o

BUY

to

w Click

m

w

w

o

.

.c

p

g

df

n

e

-x cha

Åñëè bet_cur + SB > win >= bet_cur, òî Call (èëè Check)

Если win >= bet_cur + SB, то Raise (или Bet) bet_cur — это все деньги, которые мы положили в банк за текущую игру (НЕ только за один круг торговли), плюс те деньги, которые нужно сейчас поставить.

Пояснения к bet_cur:

Если на первом круге торговли мы поставили всего $10, а на втором круге мы должны поставить $5, и в этот момент мы принимаем реше-

ние, то bet_cur равно $15 (10+5).

SB (Small Blind) — размер малого блайнда «(первая ставка вслепую — прим. ред.)». Это значение прибавляется к bet_cur во втором условии, так как мы можем увеличивать ставку только на число, кратное малому блайнду. Следовательно, если win > bet_cur, но при этом win < bet_cur + SB, то увеличивать ставку нам не выгодно, так как пришлось выста-

вить bet_cur + SB.

BB (Big Blind) - размер большого блайнда.

Стоит также добавить, что эта формула может использоваться только в компьютерной игре — в жизни за столом вряд ли будет возможность самому вычислять вероятность выигрыша и все это считать. Поэтому при реальной игре используется вычисление аутов (outs) и одсов (odds). Существуют таблицы для облегчения этих вычислений, которые можно найти в любой книжке по покеру и/или в интернете.

ОБЪЯСНЕНИЕ ФОРМУЛЫ

Изэтой формулымыполучаемматематическоеожиданиевыигрыша(в нашей формулеэтоwin). Иначеговоря, разыгрываямногоразигру с такой вероятностью (с такими же картами у наснаруках и настоле) и таким банком, мыполучимтакой выигрыш. Следовательно, чтобыоставатьсяв плюсе, мыдолжныставитьнебольше этоговыигрыша (нарисункеэто bet1). Если поставимбольше, то окажемсявминусе— выиграемменьше, чембудем ставить(нарисунке этоbet2). Стоитсделатьзамечаниек формуле — так как покер-румы берут комиссиюс каждогобанка, то нужноразмер банкауменьшить навеличинуэтойкомиссии. Формулас комиссиейбудеттакова:

p*pot*0,91 = win

РАСЧЕТ ВЕРОЯТНОСТИ

Если размер банка и размер ставки величины вполне известны, то как узнать вероятность выигрыша? На этот вопрос есть несколько ответов:

1)Узнать, сколько комбинаций хуже нашей текущей руки, и разделить это число на количество комбинаций;

2)Промоделировать несколько сотен тысяч раз ситуацию со своими картами на руках и на столе и со случайными картами у оппонентов, затем разделить число выигрышных раундов на их общее количество (набор методов, в которых используются случайные числа и большое число повторений, называются методами Монте-Карло. Да, да название произошло от знаменитого казино в Лас-Вегасе).

Плюс первого метода — самая высокая точность.

Минусы — большое потребление памяти и сложность предварительного расчета. Так как всего комбинаций может быть 2.598.960 (число сочетаний 5 карт из 52), в памяти это будет занимать примерно 10Мб (из расчета, что на одну комбинацию отводится 4 байта для хранения вероятности). Несмотря на то, что это значение уменьшится, поскольку некоторые комбинации будут одинаковы по силе и их можно будет свести в одну (за счет того, что масти в этих комбинациях не важны), этот метод мы пока рассматривать не будем, а перейдем к следующему. Плюс второго метода — простота реализации.

Минусы — при малом числе раундов не очень точен, а большое число раундов требует больше ресурсов процессора.

bet 1

win

bet2

pot

Пример распределения выигрыша при большом количестве игр

КОДИРУЕМ РАСЧЕТ ВЕРОЯТНОСТИ

На данный момент можно найти множество библиотек для расчета вероятности выигрыша. Есть как бесплатные, так и платные версии. А мы изобретем свой велосипед. Почему? Чтобы разобраться в этом вопросе детальнее и из-за желания сделать полностью своего бота. Здесь я дам краткие пояснения к коду. Он написан на Java и снабжен документацией в виде JavaDoc, так что ты без труда сможешь разобраться в нем.

Замечания по кодированию карт:

Всего 52 карты = 4 масти x 13 карт каждой масти. Масть текущей карты = <порядковый номер карты>/4 (/ — целочисленное деление). Достоинство текущей карты = <порядковый номер карты>%13 (% - остаток от деления).

Собственно, у нас должно быть 10 функций по определению комбинации. Назовем их так:

isHighCard, isOnePair, isTwoPair, isSet, isStraight, isFlush, isFullHouse, isQuads, isStraightFlush, isRoyalFlush.

Объединяет эти функции следующее — на входе подается набор карт, а на выходе — число от -1 до 12, где -1 — комбинация не найдена, от 0 до 12 — старшая карта в комбинации. 0 это двойка, 1 — тройка, и так до 12 — туз. Масть не важна. Набор карт может быть любой длины —

«На данный момент можно найти множество библиотек для расчета вероятности выигрыша. Есть как бесплатные, так и платные версии. А мы изобретем свой велосипед»

092

XÀÊÅÐ 06 /137/ 10

137_Optimized - Стр 10 (8)

hang

e

C

E

X

-

d

F

t

D

i

r

P

NOW!

o

BUY

to

w Click

m

w

w

o

.

.c

p

g

df

n

e

-xcha

hang

e

C

E

X

-

d

F

t

D

i

r

P

NOW!

o

BUY

to

w Click

m

w

w

o

.

.c

p

g

df

n

e

-x cha

ФУНКЦИЯ ОПРЕДЕЛЕНИЯ СИЛЫ КОМБИНАЦИИ

int getCombination(int[] hand, int[] board) {

int[] allCard;

if ((board == null) || (board.length == 0)) {

allCard = new int[hand.length];

System.arraycopy(hand,0,allCard,0,hand.length);

} else

{

allCard = new int[hand.length + board.length];

System.arraycopy(hand,0,allCard,0,hand.length);

System.arraycopy(board,0,allCard,hand.length,

board.length);

}

int[] card = new int[allCard.length];

int[] suite = new int[allCard.length];

int[] suiteCount = new int[4];

sortHand(allCard, card, suite, suiteCount);

if (isRoyalFlush(card, suite, suiteCount

) != -1) {

return

117;

}

int result = isStraightFlush(card, suite,

suiteCount);

if (result != -1) {

return

104 + result;

}

result

= isQuads(card);

Покерный калькулятор

if (result != -1) {

return

91 + result;

}

от 1 до 7. Это сделано для того, чтобы можно было легко рассчитать

result

= isFullHouse(card);

вероятности и в пре-флопе (когда карты на стол еще не положили, но

if (result != -1) {

раздали карты игрокам), и в терне и ривере (когда на стол положили

return 78 + result;

четыре и пять карт соответственно). То есть теперь нам нужно будет

}

дать функции список всех карт, которые лежат на столе и у игрока, а

result = isFlush(card, suite, suiteCount);

функция из этого набора карт выделит комбинацию.

if (result != -1) {

Чтобы оптимизировать определение комбинаций, мы сделаем следующее:

return

65 + result;

1) Упорядочим комбинации по убыванию достоинства карт;

}

2) Разделим входной массив карт на три: массив с достоинствами

result

= isStraight(card);

карт, массив с мастями карт и массив с количеством карт каждой

if (result != -1) {

масти.

return 52 + result;

Эти действия будет выполнять функция sortHand (см. врезку).

}

Входной параметр — массив hand, в котором хранится список карт.

result

= isSet(card);

Выходные параметры — массивы card (достоинства карт), suite (масти

if (result != -1) {

карт), suiteCount (количество карт каждой масти). В первой части

return

39 + result;

функции происходит простая сортировка заменой (можно заменить ее

}

на быструю сортировку). Тут есть маленький нюанс — сортируются не

result

= isTwoPair(card);

сами карты, а их достоинства. Чтобы узнать достоинство карты, нужно

if (result != -1) {

узнать остаток от деления на 13. Дальше, если достоинства карт оди-

return 26 + result;

наковы, то проверяем, чтобы карты с большим порядковым номером

}

(карты, у которых номер масти больше) были «левее» карт с меньшим

result

= isOnePair(card);

номером. После сортировки вычисляем достоинства карт (массив

if (result != -1) {

card), масти карт (массив suite) и количество карт каждой масти (мас-

return

13 + result;

сив suiteCount).

}

return

isHighCard(card);

Функция сортировки массива карт

}

void sortHand(int[] hand, int[] card,

int[] suite, int[] suiteCount) {

for (int i = 0; i < hand.length; i++) {

}

for (int j = 0; j < hand.length - 1; j++) {

if ((hand[j] % 13 == hand[j + 1] % 13) &&

int t;

(hand[j] < hand[j + 1])) {

if (hand[j] % 13 < hand[j + 1] % 13) {

t = hand[j + 1];

t = hand[j + 1];

hand[j + 1] = hand[j];

hand[j + 1] = hand[j];

hand[j] = t;

hand[j] = t;

}

XÀÊÅÐ 06 /137/ 10

093

137_Optimized - Стр 10 (9)

hang

e

C

E

X

-

d

F

t

D

i

r

P

NOW!

o

BUY

to

w Click

m

w

w

o

.

.c

p

g

df

n

e

-xcha

CODING

hang

e

C

E

X

-

d

F

t

D

i

r

P

NOW!

o

BUY

to

w Click

m

w

w

o

.

.c

p

g

df

n

e

-x cha

}

}

for (int i = 0; i < hand.length; i++) { card[i] = hand[i] % 13;

suite[i] = hand[i] / 13; suiteCount[suite[i]]++;

}

}

Рассмотрим алгоритмы определения комбинаций (код приводить не буду, так как он достаточно тривиален и его всегда можно найти на диске):

isHighCard

Возвращаем первую карту массива. isOnePair

Ищем две одинаковые карты, идущие подряд, и возвращаем достоинство этих карт.

isTwoPair

Ищем две одинаковые карты, идущие подряд, ищем еще две одинаковых карты и из двух достоинств этих карт возвращаем максимальное.

isSet

Ищем три одинаковые карты, идущие подряд, и возвращаем достоинство этих карт.

isStraight

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

isFlush

Проверяем, чтобы было пять карт одной масти, и возвращаем достоинство старшей карты этой масти. isFullHouse

Ищем три карты одного достоинства и еще две одинаковые карты. Возвращаем достоинство трех карт.

isQuads

Ищем две карты одного достоинства, идущие подряд. Возвращаем достоинство этих карт.

isStraightFlush

Проверяем, чтобы было пять или более карт одной масти, и чтобы эти карты шли по порядку. Возвращаем достоинство старшей карты.

isRoyalFlush

Проверяем, чтобы в комбинации были Туз, Король, Дама, Валет, 10, 9 одной масти. Возвращаем 12 (порядковый номер достоинства туза).

Кстати, еще можно оптимизировать определение комбинации — совместить проверку нескольких комбинаций в одной функции (например, проверку на две пары и пару), изменить кодирование (представление) карт в программе, перенести эти функции на C и скомпилировать в native библиотеку и т.д. Но все эти методы уменьшают наглядность кода, кроме того на данный момент скорость вычислений вполне приемлема, поэтому пока оставляем все как есть.

Функция определения силы комбинации

int getCombination(int[] hand, int[] board) { int[] allCard;

if ((board == null) || (board.length == 0)) { allCard = new int[hand.length]; System.arraycopy(hand,0,allCard,0,hand.length);

} else {

allCard = new int[hand.length + board.length]; System.arraycopy(hand,0,allCard,0,hand.length); System.arraycopy(board,0,allCard,hand.length,

НАЗВАНИЕ КАРТ В ПОКЕРЕ

Карта записывается двумя латинскими символами. Первый символ — достоинство карты, второй — масть. Карты от 2 до 9 так и записываются. T — десять (хотя иногда и просто 10), J — валет, Q — дама, K

— король, A — туз. Трефы — c, пики — s, бубны — d, червы — h.

board.length);

}

int[] card = new int[allCard.length]; int[] suite = new int[allCard.length]; int[] suiteCount = new int[4]; sortHand(allCard, card, suite, suiteCount);

if (isRoyalFlush(card, suite, suiteCount) != -1) { return 117;

}

int result = isStraightFlush(card, suite, suiteCount);

if (result != -1) { return 104 + result;

}

result = isQuads(card); if (result != -1) {

return 91 + result;

}

result = isFullHouse(card); if (result != -1) {

return 78 + result;

}

result = isFlush(card, suite, suiteCount); if (result != -1) {

return 65 + result;

}

result = isStraight(card); if (result != -1) {

return 52 + result;

}

result = isSet(card); if (result != -1) { return 39 + result;

}

result = isTwoPair(card); if (result != -1) {

return 26 + result;

}

result = isOnePair(card); if (result != -1) {

return 13 + result;

}

return isHighCard(card);

}

Далее сделаем функцию, которая возвращает абсолютную силу карточной комбинации (я назвал ее getCombination). Всего таких разных по силе комбинаций будет 118: девять комбинаций со старшими картами (по 13 старших карт в каждой комбинации), и одна комбинация без старшей карты (флеш-рояль, в котором старшая карта — всегда туз)

— 9x13+1=118. Хотя в комбинации «две пары» может быть только 12 старших карт (двойка в этой комбинации не может являться старшей), чтобы не нарушать порядок, мы это не учитываем. Эта функция нужна для последующего удобного сравнения комбинаций между собой — она возвращает число, и чем оно больше, тем сильнее комбинация.

094

XÀÊÅÐ 06 /137/ 10

137_Optimized - Стр 10 (10)

hang

e

C

E

X

-

d

F

t

D

i

r

P

NOW!

o

BUY

to

w Click

m

w

w

o

.

.c

p

g

df

n

e

-xcha

«Я написал простой калькулятор, который на основе карт на руках и на столе, а также количестве игро-

ков вычисляет вероятность выигрыша. Таких калькуляторов в Сети много и они более функциональны»

Как видно из кода, сначала функция складывает карты на руках и карты на столе

водин массив, потом сортирует его, а далее по порядку определяет комбинации. Определение идет от сильнейшей комбинации (флеш-рояль) к слабейшей (старшая карта). Чтобы комбинации отличались друг от друга, они имеют область действия — набор значений карт, находящихся в этой комбинации. Так, комбинации High card (старшая карта) соответствуют значения от 0 до 12, где 0 — это High card со старшей «двойкой», а 12 — со старшим тузом. А комбинации «пара» соответствуют значения от 13 до 25, где 13 — «пара» со старшей «двойкой», а 25 — со старшим тузом.

И, наконец, сделаем функцию для определения вероятности выигрыша (getProbabilityOfWin). Параметры этой функции следующие: свои карты, карты на столе (если есть) и количество игроков. Далее все просто — раздаем случайные карты остальным игрокам, выкладываем карты на стол (если их еще нет или не хватает) и проверяем комбинации. Если выиграли мы, то увеличиваем количество выигранных раундов на 1 (или, если есть еще игроки с такими же картами, то на 1/<количество этих игроков + 1>). Маленькое замечание по моделированию: карты игрокам лучше выдавать случайно, а не мешать колоду, а потом выдавать, так как

впервом случае будет быстрее.

ЗАКЛЮЧЕНИЕ

В принципе, описанную вероятность уже можно использовать для игры в покер в интернете. Можно разыгрывать руки,

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

Алгоритм моделирования

1)Раздать случайные карты игрокам; 2)Положить случайные карты на

стол (если на столе еще не пять карт); 3)Сравнить свою комбинацию с

комбинациями других игроков; 4)Если наша комбинация лучшая, то прибавляем к количеству выигранных раундов 1/<количество игроков с такой же комбинацией>; 5)Повторяем шаги 1-4 нужное количество раз; 6)Вероятность выигрыша равна <кол-во выигрышных

раундов>/<общее кол-во раундов>.

К коду я приложил unit-тесты, так что можно сразу проверить работоспособность всех методов.

hang

e

C

E

X

-

d

F

t

D

i

r

P

NOW!

o

BUY

to

w Click

m

w

w

o

.

.c

p

g

df

n

e

-x cha

количества игроков вероятность выигрыша уменьшается. Все правильно: чем больше игроков, тем больше вероятность, что у оппонентов карты будут лучше, чем у нас. Есть и радостная новость — все это уравнивается размером банка, поскольку он тоже будет больше. Если вспомнить формулу (p*pot = win), то получим уменьшение p с увеличением pot, то есть величина win остается неизменной. Если ты будешь использовать величину p в игре без учета размера банка, то ставь количество игроков равным 2, чтобы этот параметр не влиял на расчет. На этом я предлагаю на сегодня закруглиться, а если тебе (не)понравилось и ты (не)желаешь продолжения темы кодинга покерных ботов в следующих ][ — присылай свои отзывы мне и редактору рубрики (alexander@ real.xakep.ru). Если ты, господин наш,

читатель, изъявишь свое желание, то в следующем номере мы опишем несколько алгоритмов для принятия решений и сделаем симулятор игры в Texas Holdem No Limit Poker, в котором будут играть эти покерные алгоритмы. z

XÀÊÅÐ 06 /137/ 10

095

137_Optimized - Стр 10 (11)

hang

e

C

E

X

-

d

F

t

D

i

r

P

NOW!

o

BUY

w Click

to

CODING

m

Александр Эккерт stannic.man@gmail.co

w

w

o

.

.c

p

g

df

n

e

-xcha

hang

e

C

E

X

-

d

F

t

D

i

r

P

NOW!

o

BUY

to

w Click

m

w

w

o

.

.c

p

g

df

n

e

-x cha

EARN CASH NOW!

ÕÀÊ ÊÝØÀ WINDOWS: НОВЫЙ ВЗГЛЯД В ИНТИМНЫЕ ГЛУБИНЫ ОС

Наблюдательный программист может заметить, что разработчики Windows отказываются документировать самые вкусные и сочные места интерфейса данной ОС. И не напрасно — там скрыты поистине бесценные сокровища, от которых, к тому же, зависит безопасное функционирование операционной системы.

Íо, как известно, запретный плод сладок, и поэтому отчаянные смельчаки вроде нас с тобой вооружаются дебаггерами, отладчиками и прочими удивительными изобретениями человечества. В этой статье поговорим о такой малоизученной, с точки зрения

системного программиста или вирмейкера, вещи, как кэш файловой системы Windows.

НАЧНЕМ-Ñ…

Итак, что такое кэш Windows? Кэш или, правильнее, кэш файловой системы, представляет собой совершенно прозрачную для пользователя или программиста систему, которая находится где-то между файловой системой и виртуальной памятью ОС. Состоит он из набора функций ядра и системных потоков, которые вместе с диспетчером

096

XÀÊÅÐ 06 /137/ 10

137_Optimized - Стр 10 (12)

h

ang

e

C

E

X

-

d

F

t

D

i

r

P

NOW!

o

to

BUY

w Click

m

w

w

o

.

.c

p

g

df

n

e

-xcha

hang

e

C

E

X

-

d

F

t

D

i

r

P

NOW!

o

BUY

to

w Click

m

w

w

o

.

.c

p

g

df

n

e

-x cha

Данные будут находитьсяпо адресу 0xd4680000

Структура SECTION_OBJECT_POINTER

памяти обеспечивают кэширование данных для всех драйверов файловых систем Windows. В первую очередь кэш предназначен для увеличения производительности операционной системы за счет локального хранения (кэширования) тех данных, к которым часто обращаются пользовательские программы и подсистемы ядра ОС, а также, соответственно, для снижения количества обращений к жесткому диску. И ведь верно — стандартная процедура ввода-вывода, затрагивающая прямое обращение к жесткому диску — довольно трудоемкая операция, как с точки зрения скорости/времени, так и с точки зрения производительности. И тут дело даже не в скорости вращения дисков HDD или пропускной способности шины. В современных условиях, когда операционной системе приходится выполнять сотни операций в секунду, такой подход (прямое чтение/запись данных на жесткий диск без использования кэширования данных) выглядит малодействительным.

Наверное, я не согрешу против истины, заявив, что практически вся работа файловой системы так или иначе завязана на ее кэше. При этом работает он достаточно своеобразно: сначала полностью засоряется, после чего начинает освобождать для себя оперативную память, сбрасывая рабочие приложения в файл подкачки. Это сильно снижает скорость работы системы, особенно если на компьютере установлено менее 128 Мб ОЗУ (интересно, такие еще остались?). Мириться с этим можно, только если на твоей боевой машине не меньше гигабайта памяти. Если меньше, то проблема оказывается довольно серьезной. У диспетчера кэша есть одно необычное свойство — часть кэшируемых данных действительно находится в физической памяти. Все дело в том, что диспетчер обращается к данным, проецируя их представления (mapping file) с помощью стандартных системных вызовов WinAPI.

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

Для общего сведения стоит отметить, что диспетчер кэша способен кэшировать не только файлы, но и потоки данных — последовательности байтов в файле. Характерной особенностью кэша также является то, что у него нет собственного рабочего набора — он использует общий системный набор, в который входят кэш данных, пул подкачиваемой памяти и подкачиваемый код ядра и драйверов. Виртуальный размер кэша не отображается какими-либо счетчиками производительности, поэтому узнать его значение можно только через переменную ядра

MmSizeOfSystemCacheInPages. Обычно его размер

— 0x20000 страниц, что при странице, равной 4 Кб, будет составлять ровно 512 Мб. Для более точного отображения полного объема файловых данных, кэшируемых в системе, стандартный «Диспетчер задач» Windows показывает параметр «Системный кэш», отражающий суммарный размер системного рабочего набора и списков простаивающих и модифицированных страниц.

ЛЕЗЕМ ВГЛУБЬ

Для отслеживания изменений в кэшируемых файлах диспетчер кэша использует специальную структу-

ру — Virtual Address Control Block (VACB), которая описывает каждый 256-килобайтный слот кэша.

DVD

137_Optimized - Стр 10 (13) dvd

Надискетынайдешьсорцыизядра

Windows, реализую-

щиевзаимодействие скэшем, сорцы драйверов, которые реализуютуказанные встатьефичи, атакже многодругихвкусностей.

137_Optimized - Стр 10 (14) HTTP://WWW

137_Optimized - Стр 10 (15) links

Для совершенствования своих навыков работы с файловым кэшем Windows советую посетить www.osronline.com.

Несмотря на то, что общее направление сайта — программирование в ядре, большая часть материалов посвящена разработке драйверов

и фильтровфайловых систем. Ксожалению, наанглийском.

INFO

137_Optimized - Стр 10 (16) info

Какивсегда, дляразработкидрайверов иихотладки, качай последнююверсию

WDK — http://download. microsoft.com

XÀÊÅÐ 06 /137/ 10

097

137_Optimized - Стр 10 (17)

hang

e

C

E

X

-

d

F

t

D

i

r

P

NOW!

o

BUY

to

w Click

m

w

w

o

.

.c

p

g

df

n

e

-xcha

CODING

hang

e

C

E

X

-

d

F

t

D

i

r

P

NOW!

o

BUY

to

w Click

m

w

w

o

.

.c

p

g

df

n

e

-x cha

Показательразмерасистемного кэшав Windows

При загрузке системы диспетчер забирает часть неподкачиваемой памяти системы под свои нужды, то есть под размещение VACB. Адрес массива VACB сохраняется в переменной CcVacbs. При запросе на чтение данных из какого-либо файла диспетчер кэша должен ответить на два вопроса: находится

ли файл в кэше, и какие VACB ссылаются на запрошенный адрес. Другими словами, диспетчер должен выяснить, прое-

цируется ли представление файла на системный кэш. Для учета представлений данного файла диспетчер кэша поддерживает специальный массив указателей на VACB.Далее углубляться в структуру кэша не имеет смысла, она слишком сложна, и для ее описания просто не хватит ни места, ни времени. Если интересно, отсылаю тебя к книге «Внутреннее устройство Windows» от признанных знатоков этой операционной системы М.Руссиновича и Д.Соломона.

Разработчику система предоставляет ряд переменных и функций, непосредственно связанных с системным кэшем. Самые интересные из них — это переменная MmSystemCacheStart, которая указывает на начальный виртуальный адрес кэша, и MmSystemCacheEnd, указывающая на его конечный адрес. MmSystemCacheWs вернет нам суммарный размер системного рабочего набора. Вопрос лишь в том, как получить значение этих переменных. Их (как и кучу других не менее интересных переменных, от которых зависит работа системы) можно найти в структуре KDDEBUGGER_DATA32. Если лень искать в Сети, можешь покопаться на диске — там лежат сорцы драйвера, который реализует нужный тебе код и может показывать значения редких системных переменных.

Основные функции работы с системным кэшем — CcCopyRead, CcCopyWrite и их более быстрые аналоги CcFastCopyRead

и CcFastCopyWrite. Отличие этих функций в том, что

CcFastCopyRead и CcFastCopyWrite ограничены использованием

32-разрядных смещений внутри файла и синхронного чтения/ записи.

Диспетчер кэша вызывается драйвером файловой системы с помощью указанных функций. К примеру, при операции чтения через вызов функций CcCopyRead (CcFastCopyRead) диспетчер создает представление файла в кэше для проецирования части запрошенного файла (file mapping) и считывает файловые данные в пользовательский буфер, копируя их из представления. Поскольку драйверы файловых систем выполняются в ядре, они могут модифицировать данные, находящиеся в системном кэше, только с уведомления диспетчера кэша. Для этого предусмотрены такие функции как CcMapData, CcPinRead, CcPreparePinWrite и др., которые позволяют находить и модифицировать данные в виртуальной памяти без использования промежуточных буферов.

Экспорт системных функций ядра дляработы с кэшем

098

XÀÊÅÐ 06 /137/ 10

137_Optimized - Стр 10 (2024)
Top Articles
Latest Posts
Article information

Author: Kelle Weber

Last Updated:

Views: 6160

Rating: 4.2 / 5 (73 voted)

Reviews: 88% of readers found this page helpful

Author information

Name: Kelle Weber

Birthday: 2000-08-05

Address: 6796 Juan Square, Markfort, MN 58988

Phone: +8215934114615

Job: Hospitality Director

Hobby: tabletop games, Foreign language learning, Leather crafting, Horseback riding, Swimming, Knapping, Handball

Introduction: My name is Kelle Weber, I am a magnificent, enchanting, fair, joyous, light, determined, joyous person who loves writing and wants to share my knowledge and understanding with you.