/*
* This file is part of gwap, an open platform for games with a purpose
*
* Copyright (C) 2013
* Project play4science
* Lehr- und Forschungseinheit für Programmier- und Modellierungssprachen
* Ludwig-Maximilians-Universität München
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package gwap.game.memory;
import gwap.game.RecommendedTag;
import gwap.model.Tag;
import gwap.model.action.Tagging;
import gwap.model.resource.ArtResource;
import gwap.tools.ArtResourceFrequency;
import gwap.tools.ArtResourceFrequencyBean;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import java.util.Random;
import java.util.Set;
import javax.persistence.EntityManager;
import javax.persistence.Query;
import org.jboss.seam.ScopeType;
import org.jboss.seam.annotations.Create;
import org.jboss.seam.annotations.In;
import org.jboss.seam.annotations.Logger;
import org.jboss.seam.annotations.Name;
import org.jboss.seam.annotations.Scope;
import org.jboss.seam.international.LocaleSelector;
import org.jboss.seam.log.Log;
@Name("gwapGameMemoryAi")
@Scope(ScopeType.CONVERSATION)
public class Ai extends Player {
@In
private SharedGame gwapGameMemorySharedGame;
@In
private ReplayBean gwapGameMemoryReplayBean;
@In
private Player gwapGameMemoryPlayer;
@In private LocaleSelector localeSelector;
@In(create = true)
private ArtResourceFrequencyBean artResourceFrequencyBean;
@In
private EntityManager entityManager;
@Logger
private Log log;
@In(create = true)
private RecommendedTag recommendedTag;
private List<ArtResource> guesserResources = new ArrayList<ArtResource>();
private ArtResource lastResource = null;
private List<ArtResource> candidateResources = null;
private Set<Tag> allTags = null;
private int lastSizeDescriptions;
private int lastSizeAnswers;
private long nextAction;
private long nextActionForce;
private boolean noTags;
public Ai() {
}
@Create
public void Init() {
}
@Override
public boolean isAi() {
return true;
}
@Override
public boolean isTimedOut() {
return false;
}
private void newResource(ArtResource resource)
{
allTags=gwapGameMemoryReplayBean.allTagsForResource(resource);
if (allTags.size()<5)
noTags=true;
else
noTags=false;
lastResource=resource;
nextAction=0;
nextActionForce=new Date().getTime()+100000;
}
@Override
public synchronized String poll(int round)
{
notified();
ResourceGridBean rg=gwapGameMemorySharedGame.getResourceGridBean();
ArtResource resource=rg.getGoal();
boolean newResource=false;
if (resource!=null && resource!=lastResource)
{
newResource(resource);
newResource=true;
}
if (isGuesser())
{
if (resource!=null)
{
List<Tagging> descriptions=gwapGameMemorySharedGame.getDescriptions();
List<Question> answers=gwapGameMemorySharedGame.getAnswers();
if (newResource)
{
guesserResources=new ArrayList<ArtResource>(rg.getValidResources());
lastSizeDescriptions=descriptions.size();
lastSizeAnswers=answers.size();
}
ArtResource guess=null;
//New description added or question answered
if ((descriptions.size()!=lastSizeDescriptions ||
answers.size()!=lastSizeAnswers
|| new Date().getTime()>nextActionForce) &&
new Date().getTime()>nextAction)
{
nextActionForce=new Date().getTime()+2000+new Random().nextInt(4000);
if (descriptions.size()>0)
{
lastSizeDescriptions=descriptions.size();
lastSizeAnswers=answers.size();
Query query=entityManager.createNamedQuery("artResource.FrequencyInTagsByIdListAndTagNames");
List<Long> resIds=new ArrayList<Long>();
for (ArtResource r : guesserResources)
{
resIds.add(r.getId());
}
List<Tag> tags=new ArrayList<Tag>();
for (Tagging t : descriptions)
{
tags.add(t.getTag());
}
query.setParameter("resids", resIds);
query.setParameter("tags", tags);
query.setParameter("lang", localeSelector.getLanguage());
query.setParameter("personid", gwapGameMemoryPlayer.getPerson().getId());
List<ArtResourceFrequency> res=query.getResultList();
List<Tag> blocks=new ArrayList<Tag>();
for (Question a : answers)
{
if (a.getAnswer()==1)
{
}
else if (a.getAnswer()==2)
{
blocks.add(a.getQuestion());
}
}
if (blocks.size()>0)
{
query.setParameter("tags", blocks);
//Res2 contains the number of blocked tags that have been applied to a given resource
List<ArtResourceFrequency> res2=query.getResultList();
artResourceFrequencyBean.normalizeAll(res, 1.0/tags.size(),res2);
}
else
{
artResourceFrequencyBean.normalizeAll(res, 1.0/tags.size());
}
gwapGameMemorySharedGame.TESTsetFreq(res);
signalPartner("images");
candidateResources=null;
//Target Image is not tagged, fall back to random guessing
if (noTags)
{
if (res.size()>0)
{
if (res.get(0).getCount()>new Random().nextDouble()+0.3)
guess=res.get(new Random().nextInt(res.size())).getResource();
else
guess=rg.getGoal();
}
else
{
Random r=new Random();
if (descriptions.size()>=r.nextInt(3)) {
if (r.nextDouble()>0.2)
guess=rg.getGoal();
else
guess=guesserResources.get(new Random().nextInt(guesserResources.size()));
}
else //Take an arbitrary resource to ask questions
{
candidateResources=new ArrayList<ArtResource>();
candidateResources.add(guesserResources.get(r.nextInt(guesserResources.size())));
}
}
}
else
{
if (res.size()>0)
{
if (res.size()==1)
guess=res.get(0).getResource();
else
{
Random r=new Random();
if ((res.get(0).getCount()>=1.0 && res.get(1).getCount()<1.0) || //All tags are applied to only one resource
(res.get(0).getCount()/res.get(1).getCount()>1.2) ||
descriptions.size()>=2+r.nextInt(3))
guess=res.get(0).getResource();
else
{
candidateResources=new ArrayList<ArtResource>();
int i=0;
while (i<res.size() && res.get(0).getCount()/res.get(i).getCount()<2)
{
candidateResources.add(res.get(i).getResource());
i++;
}
}
}
}
else
{
Random r=new Random();
if (descriptions.size()>=r.nextInt(3))
{
if (r.nextDouble()>0.3)
guess=rg.getGoal();
else
guess=guesserResources.get(r.nextInt(guesserResources.size()));
}
else //Take an arbitrary resource to ask questions
{
candidateResources=new ArrayList<ArtResource>();
candidateResources.add(guesserResources.get(r.nextInt(guesserResources.size())));
}
}
}
}
}
if (guess==null && gwapGameMemorySharedGame.getAllowDescriptions() &&
(gwapGameMemorySharedGame.getAlternatingModeTurn()==1 || !gwapGameMemorySharedGame.getAlternatingMode()))
{
if ((candidateResources==null || candidateResources.size()==0) && gwapGameMemorySharedGame.getAlternatingMode()) //We have to make a guess, lest the game is blocked
{
candidateResources=new ArrayList<ArtResource>();
candidateResources.add(guesserResources.get(new Random().nextInt(guesserResources.size())));
}
if (candidateResources!=null && new Date().getTime()>nextAction)
{
nextAction=new Date().getTime()+1000+new Random().nextInt(6000);
boolean done=false;
if (candidateResources.size()>1)
{
ArtResource r=candidateResources.get(0);
Query query=entityManager.createNamedQuery("tag.uniqueByResourceList");
List<Tag> taglist=new ArrayList<Tag>();
if (descriptions.isEmpty())
taglist.add(new Tag());
else
{
for (Tagging t : descriptions)
{
taglist.add(t.getTag());
}
}
query.setParameter("language", localeSelector.getLanguage());
query.setParameter("res", r);
query.setParameter("reslist", candidateResources);
query.setParameter("taglist", taglist);
query.setMaxResults(3);
List<Tag> tags=query.getResultList();
if (tags.size()>0)
{
log.info("Found unique tags for resource: #0", r.getId());
for (Tag t:tags)
{
log.info("#0",t.getName());
}
Tag t=tags.get(new Random().nextInt(tags.size()));
setEnteredTag(t.getName());
sendQuestion(round);
done=true;
}
}
if (!done)
{
ArtResource r=candidateResources.get(new Random().nextInt(candidateResources.size()));
Set<Tag> tag=gwapGameMemoryReplayBean.allTagsForResource(r, 1);
Iterator<Tag> t=tag.iterator();
if (t.hasNext())
{
setEnteredTag(t.next().getName());
sendQuestion(round);
done=true;
}
}
if (!done)
{
guess=guesserResources.get(new Random().nextInt(guesserResources.size()));
}
}
}
if (guess!=null)
{
guesserResources.remove(guess);
//Select resource
gwapGameMemorySharedGame.resourceClicked(guess, round, this);
//Confirm
gwapGameMemorySharedGame.resourceClicked(guess, round, this);
} //Ask a question, if currently possible
}
}
else
{
//Simulate Describer (to verify tags)
if (resource==null)
{
log.info("Selecting new resource");
ArtResource next=gwapGameMemoryReplayBean.getNextResource();
if (next!=null)
{
gwapGameMemorySharedGame.resourceClicked(next, round, this);
resource=next;
}
else {
//Select a random resource
List<ArtResource> resources=new ArrayList<ArtResource>(gwapGameMemorySharedGame.getResourceGridBean().getResources());
Collections.shuffle(resources);
for (ArtResource r:resources)
{
if (r!=null)
{
resource=r;
gwapGameMemorySharedGame.resourceClicked(resource, round, this);
break;
}
}
}
newResource=true;
newResource(resource);
}
if (newResource)
{
gwapGameMemoryReplayBean.updateDescriptions(resource);
}
//Send descriptions
if (gwapGameMemorySharedGame.getAllowDescriptions() &&
(gwapGameMemorySharedGame.getAlternatingModeTurn()==0 || !gwapGameMemorySharedGame.getAlternatingMode()))
{
List<Tagging> ts;
if (gwapGameMemorySharedGame.getAlternatingMode())
ts=gwapGameMemoryReplayBean.getDescriptions(1);
else
ts=gwapGameMemoryReplayBean.getDescriptions();
for (Tagging t : ts)
{
gwapGameMemorySharedGame.sendDescription(t, round);
}
gwapGameMemoryPlayer.signal("tags");
}
//Answer questions
if (gwapGameMemorySharedGame.getAllowQuestions() &&
(gwapGameMemorySharedGame.getAlternatingModeTurn()==2 || !gwapGameMemorySharedGame.getAlternatingMode()))
{
List<Question> questions=gwapGameMemorySharedGame.getQuestions();
while (questions.size()>0)
{
Question q=questions.get(0);
//if tags are available, answer
if (!noTags)
{
if (allTags!=null && allTags.contains(q.getQuestion()))
answerQuestion(q,1, round);
else
answerQuestion(q,2, round);
}
//if there are no tags and we are in alternating mode,
//answer randomly to avoid blocking the game
else if (gwapGameMemorySharedGame.getAlternatingMode())
{
answerQuestion(q,1+new Random().nextInt(2), round);
}
}
if (gwapGameMemorySharedGame.getAlternatingMode())
{
//Wait a bit
nextAction=new Date().getTime()+2000+new Random().nextInt(5000);
}
gwapGameMemorySharedGame.getQuestions().clear();
}
}
return "";
}
}