Так, новости такие. От графа пока откажемся, слишком мало кода в интернете, все почему-то выходят на матрицу смежности, что как-то не солидно. Ну, да ладно.
Удалось развить функцию, находящую вероятность завершить строку(до этого аналогичная считала вероятность собрать фантазию в топе).
Теперь функция работает с полным перебором колоды, ей не надо напрямую сообщать кол-во оставшихся карт того или иного достоинства.
Для задачи "имеем колоду, из которой последовательно(по одной) вытаскиваем М карт, каждый раз мы можем либо оставить(добавить) карту к уже имеющимся в линии, либо сбросить. Найти вероятность собрать перечень комбинаций.
Псевдокод такой.
1. Имеем линию для завершения, например
2. Определяем успешные комбинации, например
,
и т.д.
3. Для каждого шага и для каждой карты из колоды у нас 2 альтернативы: либо оставить карту, либо сбросить.
4. Введём дополнительную функцию, которая считает полную вероятность собрать комб. из п2, с колодой без карты из п3, для случаев, когда мы её добавили и когда сбросили. Она нужна для того, что если пришла
на первом подьёме(осталось ещё 3), то может быть выгоднее её сбросить, чем положить. Так как для какой-то конкретной колоды вероятность закончить стрит за оставшиеся "подьёмы" может быть ниже, чем собрать 2пары.
5. Для п.3 вызываем доп. функцию и решаем: оставлять(добавлять) карту или скидывать.
6. Повторяем п.5 для уменьшенной на 1 карту колоды
7. Выход из функции по 2 условиям: либо (какая-то)комбинация собрана, либо мы израсходовали все "подьёмы".
Доп. функция
написана с помощью рекурсии, но можно и циклами.
// FP - колво свободных "мест"
double complete_p(std::string d, std::string s, int FP)
{
std::string foo, foo2;
double t;
std::sort(s.begin(), s.end());
if(s == "45678" || s == "34567" || s == "56789" )
//|| s == "56777" || s == "56766" || s == "55567")
return 1.0;
if(s == "56777" || s == "56667" || s == "55567")
return 1.0;
// else if(s == "56677" || s == "55667" || s == "55677")
//return 1.0;
else if (s.length() == 5)
return 0.0;
if(s.length() == 4 && FP== 1) //кладём любую пришедшую карту
{
for(int i=0; i < d.size(); ++i)
{foo = s + d.at(i);
foo2 = d;
foo2.erase(foo2.begin() + i);
t = t + 1.0/(double)d.size()*complete_p(foo2,foo,FP-1);
}
return t;
}
//} while(d.size() >0);
if(s.length() == 3 && FP== 2) //кладём любую пришедшую карту
{
for(int i=0; i < d.size(); ++i)
{foo = s + d.at(i);
foo2 = d;
foo2.erase(foo2.begin() + i);
t = t + 1.0/(double)d.size()*complete_p(foo2,foo,FP-1);
}
return t;
}
};
// M - колво подьёмов по одной карте
double fill_p(std::string d, std::string s, int M)
{
std::string foo, foo2;
double t =0.0;
std::sort(s.begin(), s.end());
if(s == "45678" || s == "34567" || s == "56789" )
//|| s == "56777" || s == "56766" || s == "55567")
return 1.0;
if(s == "56777" || s == "56667" || s == "55567")
return 1.0;
//else if(s == "56677" || s == "55667" || s == "55677")
//return 1.0;
else if (s.length() == 5)
return 0.0;
//кладём или не кладём?
if (s.length() == 4 && M == 1)
{
for(int i=0; i < d.size(); ++i)
{
foo = s + d.at(i);
foo2 = d;
foo2.erase(foo2.begin() + i);
//кладём по-любому
t = t + 1.0/(double)d.size()*fill_p(foo2,foo,M-1);
}return t;
}
//тоже кладём по-любому
if (s.length() == 3 && M == 2)
{
for(int i=0; i < d.size(); ++i)
{
foo = s + d.at(i);
foo2 = d;
foo2.erase(foo2.begin() + i);
t = t + 1.0/(double)d.size()*fill_p(foo2,foo,M-1);
}return t;
}
if ( M > 2 && s.length() == 3)
{for(int i=0; i < d.size(); ++i)
{
foo = s + d.at(i);
foo2 = d;
foo2.erase(foo2.begin() + i);
if (complete_p(foo2,foo,1) >= complete_p(foo2,s,2))
t = t + 1.0/(double)d.size()*fill_p(foo2,foo,M-1);
else
t = t + 1.0/(double)d.size()*fill_p(foo2,s,M-1);
}
return t;
}
if ( M >= 2 && s.length() == 4)
{for(int i=0; i < d.size(); ++i)
{
foo = s + d.at(i);
foo2 = d;
foo2.erase(foo2.begin() + i);
if (complete_p(foo2,foo,0) >= complete_p(foo2,s,1))
t = t + 1.0/(double)d.size()*fill_p(foo2,foo,M-1);
else
t = t + 1.0/(double)d.size()*fill_p(foo2,s,M-1);
}
return t;
}
};
Перехожу к отладке подьёмов из 2 карт{можно положить 0, 1 или 2карты}
Поигрываю в китайский, но всегда интересовало точное решение. В связи с этим запустил C++ Builder и решил немного поиграться.
Так как не запускал его достаточно давно, пропустил переход С++ на С++ 11 (и тем более 14). Но, делать нечего, пока поработаем на старом.
Конкретно эту тему решил завести для того, чтобы отслеживать, что получилось, а что нет. Так как выбранную логику по мере обкатки приходится переписывать чуть ли не заново, здесь я буду вести учёт своим успехам\неудачам(под новую логику приходится переписывать весь код и один раз я уже забыл, как работает предыдущий )
Итак, версия 1.0
- Колода и рука представляют собой символьные строки. (Мастей нет)
- любая линия(5ти или 3ех карточный набор) представляет из себя какую-либо комбинацию(2 пары, старшая карта и т.д.)
- Ход осуществляется как выборка 3ёх произвольных карт из колоды и нахождения совокупности всех возможных раскладов по линиям.
Ну, тут какие достижения. 2 подьёма прожка считает за секунды, 3 подьёма уже за около 4 минут.
Ясно, что посчитать на ней стартовый расклад не удастся(да там и памяти не хватит)
Версия 2.0 (в работе)
- для комбинаций введён ранг(старшинство при сравнении одинаковых по значению). Мастей по-прежнему нет
- для стартового набора введено понятие потенциала(для 2ух линий это все возможные руки, где комбинация внизу существует и старше комбинации выше).
- также на каждом подьёме считается вероятность собрать фантазию.
- на каком-либо подьёме придётся сделать выбор: сделать упор на сбор фантазии ИЛИ сосредоточиться на "сборе" очков. В конечном итоге это будет тоже самое ЕВ, но можно будет сделать отсечку вручную
Планируется
- для класса "рука" ввести функцию (метод) проверки "жива ли рука"
- Жёстко связать колоду и руку игрока(руки игроков). Сейчас они связаны вручную.
Ну, и наконец пример. Как логика на стадии проектирования даёт сбой. Я уже писал, что мастей пока нет. Но нет также и истории развития партии. Как планировалось определить лучшее разложение, ну скажем, 3его подьёма? Планировалось найти все варианты завершения руки, вернуться обратно и в отсортированном массиве найти 2 карты и 2 места, куда их следует положить. Однако, так как истории нету, то возможность отличить, на 2ом подьёме положена 7ка или на 5ом нет(ведь это могла быть 7ка, которую мы положили, а может быть вновь пришедшая, если мы в рассматриваемом подьёме её сбросили). Соответственно, и возвращаться некуда.
Буду благодарен за вашу подсказку, как решить эту проблему.
Пока на ум приходит только каждую руку рассматривать как {разложение стартового набора} +..+{разложение i-ого набора}
Вот только не придётся ли в итоге всё равно перейти к просто руке, неважно каким именно путём она была собрана.
Да, и пример.
Для данной руки я считал расклад с наибольшим ЕВ
пришли сброшены
ясно, что можно положить либо туз вниз для стрита, либо надеяться собрать 2пары+ на
ЕВ туз вниз получилось 1.57 очка
ЕВ тув в топ - 0.64
Если кто подтвердит цифры, буду премного благодарен.