File indexing completed on 2024-05-12 05:39:27

0001 /***************************************************************************
0002  * Copyright (C) 2014 by Renaud Guezennec                                   *
0003  * https://rolisteam.org/contact                      *
0004  *                                                                          *
0005  *  This file is part of DiceParser                                         *
0006  *                                                                          *
0007  * DiceParser is free software; you can redistribute it and/or modify       *
0008  * it under the terms of the GNU General Public License as published by     *
0009  * the Free Software Foundation; either version 2 of the License, or        *
0010  * (at your option) any later version.                                      *
0011  *                                                                          *
0012  * This program is distributed in the hope that it will be useful,          *
0013  * but WITHOUT ANY WARRANTY; without even the implied warranty of           *
0014  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the             *
0015  * GNU General Public License for more details.                             *
0016  *                                                                          *
0017  * You should have received a copy of the GNU General Public License        *
0018  * along with this program; if not, write to the                            *
0019  * Free Software Foundation, Inc.,                                          *
0020  * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.                 *
0021  ***************************************************************************/
0022 #include "groupnode.h"
0023 #include "result/diceresult.h"
0024 //-------------------------------
0025 int DieGroup::getSum() const
0026 {
0027     int sum= 0;
0028     for(int i : *this)
0029     {
0030         sum+= i;
0031     }
0032     return sum;
0033 }
0034 
0035 void DieGroup::removeValue(DieGroup i)
0036 {
0037     for(auto x : i)
0038     {
0039         removeOne(x);
0040     }
0041 }
0042 
0043 int DieGroup::getLost() const
0044 {
0045     return getSum() - m_exceptedValue;
0046 }
0047 
0048 qint64 DieGroup::getExceptedValue() const
0049 {
0050     return m_exceptedValue;
0051 }
0052 
0053 void DieGroup::setExceptedValue(qint64 exceptedValue)
0054 {
0055     m_exceptedValue= exceptedValue;
0056 }
0057 
0058 //---------------------
0059 GroupNode::GroupNode(bool complexOutput)
0060     : m_scalarResult(new ScalarResult), m_stringResult(new StringResult), m_complexOutput(complexOutput)
0061 {
0062 }
0063 
0064 void GroupNode::run(ExecutionNode* previous)
0065 {
0066     if(m_complexOutput)
0067         m_result= m_stringResult;
0068     else
0069         m_result= m_scalarResult;
0070 
0071     m_previousNode= previous;
0072     if(nullptr != previous)
0073     {
0074         m_result->setPrevious(previous->getResult());
0075         Result* tmpResult= previous->getResult();
0076         if(nullptr != tmpResult)
0077         {
0078             DiceResult* dice= dynamic_cast<DiceResult*>(tmpResult);
0079             if(nullptr != dice)
0080             {
0081                 auto list= dice->getResultList();
0082                 DieGroup allResult;
0083                 for(auto& die : list)
0084                 {
0085                     allResult << die->getListValue();
0086                 }
0087                 std::sort(allResult.begin(), allResult.end(), std::greater<qint64>());
0088                 if(allResult.getSum() > m_groupValue)
0089                 {
0090                     auto copy= allResult;
0091                     auto const die= getGroup(allResult);
0092 
0093                     for(auto list : die)
0094                     {
0095                         for(auto val : list)
0096                         {
0097                             copy.removeOne(val);
0098                         }
0099                     }
0100                     m_scalarResult->setValue(die.size());
0101                     QStringList list;
0102                     for(auto group : die)
0103                     {
0104                         QStringList values;
0105                         std::transform(group.begin(), group.end(), std::back_inserter(values),
0106                                        [](qint64 val) { return QString::number(val); });
0107                         list << QStringLiteral("{%1}").arg(values.join(","));
0108                     }
0109                     QStringList unused;
0110                     std::transform(copy.begin(), copy.end(), std::back_inserter(unused),
0111                                    [](qint64 val) { return QString::number(val); });
0112                     if(!unused.isEmpty())
0113                         m_stringResult->addText(
0114                             QStringLiteral("%1 (%2 - [%3])").arg(die.size()).arg(list.join(",")).arg(unused.join(",")));
0115                     else
0116                         m_stringResult->addText(QStringLiteral("%1 (%2)").arg(die.size()).arg(list.join(",")));
0117                 }
0118                 else
0119                 {
0120                     m_scalarResult->setValue(0);
0121                 }
0122             }
0123         }
0124     }
0125     if(nullptr != m_nextNode)
0126     {
0127         m_nextNode->run(this);
0128     }
0129 }
0130 
0131 QString GroupNode::toString(bool withLabel) const
0132 {
0133     if(withLabel)
0134     {
0135         return QString("%1 [label=\"SplitNode Node\"]").arg(m_id);
0136     }
0137     else
0138     {
0139         return m_id;
0140     }
0141 }
0142 qint64 GroupNode::getPriority() const
0143 {
0144     qint64 priority= 0;
0145     if(nullptr != m_nextNode)
0146     {
0147         priority= m_nextNode->getPriority();
0148     }
0149     return priority;
0150 }
0151 ExecutionNode* GroupNode::getCopy() const
0152 {
0153     GroupNode* node= new GroupNode(m_complexOutput);
0154     if(nullptr != m_nextNode)
0155     {
0156         node->setNextNode(m_nextNode->getCopy());
0157     }
0158     return node;
0159 }
0160 
0161 int GroupNode::getGroupValue() const
0162 {
0163     return m_groupValue;
0164 }
0165 
0166 void GroupNode::setGroupValue(qint64 groupValue)
0167 {
0168     m_groupValue= groupValue;
0169 }
0170 
0171 bool GroupNode::composeWithPrevious(DieGroup previous, qint64 first, qint64 current, DieGroup& addValue)
0172 {
0173     if(previous.getSum() + first + current == m_groupValue)
0174     {
0175         addValue.append(previous);
0176         addValue.append(first);
0177         addValue.append(current);
0178         return true;
0179     }
0180 
0181     if(previous.isEmpty())
0182         return false;
0183 
0184     int maxComboLength= previous.size();
0185     bool hasReachMax= false;
0186 
0187     QList<DieGroup> possibleUnion;
0188     for(auto va : previous)
0189     {
0190         DieGroup dieG;
0191         dieG.append(va);
0192         possibleUnion.append(dieG);
0193     }
0194 
0195     while(!hasReachMax)
0196     {
0197         auto tmpValues= previous;
0198         QList<DieGroup> possibleTmp;
0199         for(auto& diaG : possibleUnion)
0200         {
0201             if(tmpValues.isEmpty())
0202                 break;
0203             tmpValues.removeValue(diaG);
0204 
0205             for(auto& value : tmpValues)
0206             {
0207                 DieGroup dia;
0208                 dia.append(diaG);
0209                 dia.append(value);
0210                 if(dia.size() >= maxComboLength - 1)
0211                     hasReachMax= true;
0212                 else
0213                     possibleTmp.append(dia);
0214             }
0215         }
0216         if(possibleTmp.isEmpty())
0217             hasReachMax= true;
0218         else
0219         {
0220             possibleTmp.append(possibleUnion);
0221             possibleUnion= possibleTmp;
0222         }
0223     }
0224     std::sort(possibleUnion.begin(), possibleUnion.end(),
0225               [=](const DieGroup& a, const DieGroup& b) { return a.getLost() > b.getLost(); });
0226     bool found= false;
0227     for(int i= 0; (!found && i < possibleUnion.size()); ++i)
0228     {
0229         auto& value= possibleUnion.at(i);
0230         if(value.getSum() + current + first >= m_groupValue)
0231         {
0232             addValue << value << current << first;
0233             found= true;
0234         }
0235     }
0236     return found;
0237 }
0238 
0239 QList<DieGroup> GroupNode::getGroup(DieGroup values)
0240 {
0241     if(values.isEmpty())
0242         return {};
0243 
0244     auto first= values.takeFirst();
0245 
0246     QList<DieGroup> result;
0247     QMap<qint64, DieGroup> loseMap;
0248     if(first >= m_groupValue)
0249     {
0250         DieGroup group;
0251         group << first;
0252         loseMap[0]= group;
0253     }
0254     else
0255     {
0256         auto it= values.rbegin();
0257         bool foundPerfect= false;
0258         qint64 cumuledValue= 0;
0259         DieGroup previousValue;
0260         while((values.rend() != it) && !foundPerfect)
0261         {
0262             if(first + *it == m_groupValue)
0263             {
0264                 foundPerfect= true;
0265                 DieGroup group;
0266                 group << first << *it;
0267                 loseMap[0]= group;
0268             }
0269             else if(first + *it > m_groupValue)
0270             {
0271                 DieGroup group;
0272                 group << first << *it;
0273                 loseMap[first + *it - m_groupValue]= group;
0274             }
0275             else if(first + *it + cumuledValue == m_groupValue)
0276             {
0277                 DieGroup group;
0278                 group << first << *it << previousValue;
0279                 foundPerfect= true;
0280                 loseMap[0]= group;
0281             }
0282             else if(first + *it + cumuledValue > m_groupValue)
0283             {
0284                 DieGroup group;
0285                 group.setExceptedValue(m_groupValue);
0286                 auto b= composeWithPrevious(previousValue, first, *it, group);
0287                 if(b)
0288                     loseMap[group.getLost()]= group;
0289             }
0290             previousValue << *it;
0291             cumuledValue+= *it;
0292             ++it;
0293         }
0294     }
0295     if(!loseMap.isEmpty())
0296     {
0297         DieGroup die= loseMap.first();
0298         result.append(die);
0299         DieGroup valueToRemove= die;
0300         if(!valueToRemove.isEmpty())
0301         {
0302             valueToRemove.removeFirst();
0303             values.removeValue(valueToRemove);
0304 
0305             if(values.getSum() >= m_groupValue)
0306             {
0307                 result.append(getGroup(values));
0308             }
0309         }
0310     }
0311     return result;
0312 }