/* * 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.model.GameRound; import gwap.model.Tag; import gwap.model.action.Tagging; import gwap.model.resource.ArtResource; import gwap.model.resource.Resource; import gwap.tools.DBLimiter; import gwap.tools.Timer; import java.io.Serializable; import java.util.ArrayList; import java.util.Collections; import java.util.Date; import java.util.HashSet; 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.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("gwapGameMemoryReplayBean") @Scope(ScopeType.CONVERSATION) public class ReplayBean implements Serializable { @Logger Log log; @In EntityManager entityManager; @In Player gwapGameMemoryPlayer; @In PlayerMatcher gwapGameMemoryPlayerMatcher; @In SharedGame gwapGameMemorySharedGame; @In LocaleSelector localeSelector; @In(create=true) private DBLimiter dbLimiter; //Rounds already selected for a forced replay private List<Long> forceRounds=new ArrayList<Long>(); private long forcePerson=0; private Long replayRoundId; private Date replayRoundStart; private Date roundStart; private List<ArtResource> resourcesPlayed; private List<Tagging> descriptions; public Long getReplayRoundId() { return replayRoundId; } public List<ArtResource> updatePreviousRound(long count, long forceId) { //Get a random round try { if (forceId!=0) { Query query; if (forceRounds.isEmpty()) { query = entityManager.createNamedQuery("gameRound.FromSession"); query.setParameter("sessionid", forceId); } else { query = entityManager.createNamedQuery("gameRound.FromSessionNotIdList"); query.setParameter("sessionid", forceId); query.setParameter("limitlist", forceRounds); query.setParameter("personid", forcePerson); } List<GameRound> rounds=query.getResultList(); if (rounds.isEmpty()) replayRoundId=0L; else { GameRound round=rounds.get(0); replayRoundId=round.getId(); forcePerson=round.getPerson().getId(); forceRounds.add(replayRoundId); } } if (forceId==0 || replayRoundId==0) { Query lquery = entityManager.createNamedQuery("gameRound.randomIdByGameTypeIdAndNotPersonId"); lquery.setParameter("personid", gwapGameMemoryPlayer.getPerson().getId()); lquery.setParameter("gametypeid", gwapGameMemorySharedGame.getGameType().getId()); Query query = entityManager.createNamedQuery("gameRound.AtLeastTaggingsLimit"); query.setParameter("limit", 5L); query.setParameter("lang", localeSelector.getLanguage()); query.setMaxResults(100); dbLimiter.PrepareLimitedQuery(query, lquery); Timer t=new Timer(); List<Long> res=query.getResultList(); if (res.size()==0) { log.info("No previous rounds found"); return null; } log.info("gameRound.AtLeastTaggingsLimit query time: #0", t.timePassed()); replayRoundId=res.get(new Random().nextInt(res.size())); } Query query = entityManager.createNamedQuery("artResource.resourcesByGameRoundId"); query.setParameter("id", replayRoundId); List<ArtResource> result=query.getResultList(); if (result.size()!=count) { log.info("GameRound #0 has invalid number of Resources: #1", replayRoundId, result.size()); replayRoundId=0L; return null; } resourcesPlayed=getPlayedResources(); return result; } catch(Exception e) { log.info("No previous rounds found: "+e.getMessage()); return null; } /* try { Query query = entityManager.createNamedQuery("gameRound.randomGameRoundByResourceCountAndLimit"); query.setParameter("count", count); query.setParameter("limit", 2L); query.setParameter("personid", gwapGameMemoryPlayer.getPerson().getId()); query.setParameter("gametypeid", gwapGameMemoryPlayerMatcher.getGameType().getId()); query.setParameter("lang", localeSelector.getLanguage()); query.setMaxResults(100); List<Long> res=query.getResultList(); if (res.size()==0) { log.info("No previous rounds found"); return null; } //Randomly replay one of the last 100 gamerounds replayRoundId=res.get(new Random().nextInt(res.size())); resourcesPlayed=getPlayedResources(); query = entityManager.createNamedQuery("artResource.resourcesByGameRoundId"); query.setParameter("id", replayRoundId); return query.getResultList(); } catch(Exception e) { log.info("No previous rounds found: "+e.getMessage()); return null; }*/ } public List<ArtResource> getResourcesPlayed() { return resourcesPlayed; } private List<ArtResource> getPlayedResources() { Query query = entityManager.createNamedQuery("artResource.playedResourceIdsByGameRoundId"); query.setParameter("id", replayRoundId); List<Long> results=query.getResultList(); if (results.size()>0) { ArrayList<ArtResource> resourcesPlayed=new ArrayList<ArtResource>(); //This a bit of a hack, because the previous query cannot return actual objects //(see https://forum.hibernate.org/viewtopic.php?p=2273602 ) for(Long result : results) { //query.setParameter("id", (Long)result); //resourcesPlayed.add((ArtResource)query.getSingleResult()); resourcesPlayed.add(entityManager.find(ArtResource.class, result)); } return resourcesPlayed; } else return null; } public ArtResource getNextResource() { if (resourcesPlayed!=null && resourcesPlayed.size()>0) return resourcesPlayed.remove(0); else return null; } public Set<Tag> allTagsForResource(Resource r) { return allTagsForResource(r, 0); } public Set<Tag> allTagsForResource(Resource r, int limit) { Set<Tag> resultSet=new HashSet<Tag>(); Query query = entityManager.createNamedQuery("tagging.randomTagByResourceAndLanguage"); query.setParameter("resource", r); query.setParameter("language", localeSelector.getLanguage()); if (limit>0) query.setMaxResults(limit); List<Tag> res=query.getResultList(); resultSet.addAll(res); return resultSet; } public void updateDescriptions(Resource r) { log.info("Updating descriptions for resource #0", r.getId()); try { Query query = entityManager.createNamedQuery("tagging.taggingsByGameAndResourceId"); query.setParameter("resid", r.getId()); query.setParameter("gameid", replayRoundId); descriptions=query.getResultList(); } catch (Exception e) { descriptions=new ArrayList<Tagging>(); } if (descriptions.size()>5) { replayRoundStart=descriptions.get(0).getCreated(); log.info("updateDescriptions() for resource "+r.getId()+" found: "+descriptions.size()); } else { log.info("updateDescriptions() for resource failed, not enough descriptions found (only "+descriptions.size()+"). Adding random taggings"); /*Query query = entityManager.createNamedQuery("tagging.randomTagByResourceAndLanguage"); query.setParameter("resource", r); query.setParameter("language", localeSelector.getLanguage()); query.setMaxResults(15);*/ Random random = new Random(); Set<Tag> tags=allTagsForResource(r, 15); //Make sure no tag appears twice Set<Tag> descTags=new HashSet<Tag>(); for (Tagging t : descriptions) { descTags.add(t.getTag()); log.info("Already existing: #0 id: #1", t.getTag().getName(), t.getTag().getId()); } tags.removeAll(descTags); List<Tag> tags2=new ArrayList<Tag>(); tags2.addAll(tags); Collections.shuffle(tags2); Date date; if (descriptions.size()>0) { replayRoundStart=descriptions.get(0).getCreated(); //Get last item's creation date and add new tags after it date=descriptions.get(descriptions.size()-1).getCreated(); } else { replayRoundStart=new Date(); date=replayRoundStart; } boolean firstTag=true; for (Tag t : tags2) { date=new Date(date.getTime()); if (firstTag) //Send the first tag faster date.setTime(date.getTime()+500+t.getName().length()*(100+random.nextInt(150))+random.nextInt(1000)); else date.setTime(date.getTime()+1000+t.getName().length()*(200+random.nextInt(300))+random.nextInt(2000)); firstTag=false; Tagging tg=new Tagging(); tg.setTag(t); tg.setResource(r); tg.setCreated(date); descriptions.add(tg); } log.info("Added "+descriptions.size()+" random tags."); } roundStart=new Date(); log.info("Added descriptions:"); for (Tagging t:descriptions) { log.info("#0", t.getTag()); } } public List<Tagging> getDescriptions() { return getDescriptions(0); } public List<Tagging> getDescriptions(int limit) { if (descriptions!=null) { List<Tagging> res=new ArrayList<Tagging>(); if (descriptions.size()>0) { Tagging d=descriptions.get(0); int i=0; while (d.getCreated().getTime()-replayRoundStart.getTime() <= new Date().getTime()-roundStart.getTime() && (limit==0 || i<limit)) { res.add(d); descriptions.remove(0); if (descriptions.size()>0) d=descriptions.get(0); else break; i++; } } log.info("getDescriptions() found: "+res.size()+" remaining: "+descriptions.size()); return res; } else return new ArrayList<Tagging>(); } }