/* * 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.elearn; import gwap.game.AbstractGameSessionBean; import gwap.model.GameConfiguration; import gwap.model.Highscore; import gwap.model.Tag; import gwap.model.action.Tagging; import gwap.model.resource.Term; import gwap.tools.TagSemantics; import gwap.widget.HighscoreBean; import gwap.wrapper.MatchingTag; import gwap.wrapper.HighscoreSet; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Map; import javax.persistence.NoResultException; import javax.persistence.NonUniqueResultException; import javax.persistence.Query; import org.jboss.seam.Component; import org.jboss.seam.ScopeType; import org.jboss.seam.annotations.In; import org.jboss.seam.annotations.Name; import org.jboss.seam.annotations.Out; import org.jboss.seam.annotations.Scope; import org.jboss.seam.international.LocaleSelector; /** * @author Katharina Krug, Fabian Kneißl */ @Name("elearnTermina") @Scope(ScopeType.CONVERSATION) public class Termina extends AbstractGameSessionBean { private static final int WRONG_ASSOCIATION_SCORE = -1; private static final long serialVersionUID = -4140570198882726459L; @In(required=false) @Out(required=false) protected GameConfiguration gameConfiguration; @In(create=true) protected TermBean elearnTermBean; @In(required=false) @Out(required=false) protected Term term; @In protected LocaleSelector localeSelector; @In(create=true) protected Map<String, String> messages; protected GameConfiguration nextGameConfiguration; protected String association; protected Integer foundAssociations; protected List<Tag> tags; protected List<String> answers; protected List<Tag> previousTaggings; protected MatchingTag lastAssociation; protected boolean paletteMode; public boolean isPaletteMode() { return paletteMode; } public List<Tag> getPreviousTaggings() { return previousTaggings; } @Override public void startGameSession() { if (gameConfiguration != null) nextGameConfiguration = gameConfiguration; startGameSession("elearnTermina"); } public void endGameByUser() { super.endRound(); endGameSession(); } @Override public boolean startRound() { if (!super.startRound()) return false; lastAssociation = null; previousTaggings = new ArrayList<Tag>(); foundAssociations = 0; adjustGameConfiguration(); gameRound.setGameConfiguration(gameConfiguration); term = elearnTermBean.updateTerm(gameConfiguration); gameRound.getResources().add(term); return true; } protected void adjustGameConfiguration() { if (nextGameConfiguration != null) { gameConfiguration = nextGameConfiguration; } else if (gameConfiguration == null) { gameConfiguration = new GameConfiguration(); // gameConfiguration.setLevel(1); gameConfiguration.setBid(2); gameConfiguration.setRoundDuration(60); } // New method: Level gets updated if no more terms exist (see endRound) // gameConfiguration.setLevel(1 + ((gameRound.getNumber()-1) / 10)); // find GameConfiguration if it exists, otherwise create it try { Query q; if (gameConfiguration.getLevel() != null) { if (gameConfiguration.getTopic() == null) { q = entityManager.createNamedQuery("gameConfiguration.byAllWithoutTopic"); } else { q = entityManager.createNamedQuery("gameConfiguration.byAll"); q.setParameter("topic", gameConfiguration.getTopic()); } q.setParameter("level", gameConfiguration.getLevel()); } else { if (gameConfiguration.getTopic() == null) { q = entityManager.createNamedQuery("gameConfiguration.byBidAndRoundDuration"); } else { q = entityManager.createNamedQuery("gameConfiguration.byTopicBidAndRoundDuration"); q.setParameter("topic", gameConfiguration.getTopic()); } } q.setParameter("bid", gameConfiguration.getBid()); q.setParameter("roundDuration", gameConfiguration.getRoundDuration()); q.setMaxResults(1); gameConfiguration = (GameConfiguration) q.getSingleResult(); } catch (NoResultException e) { entityManager.persist(gameConfiguration); } nextGameConfiguration = GameConfiguration.deepCopy(gameConfiguration); } @Override protected void loadNewResource() { // this is done in the other two methods } public void startRoundPalette() { startRound(); this.paletteMode = true; // 1. Create list of shown tags int maxNrResults = gameConfiguration.getBid(); // Could be selected in a more intelligent way :) List<Tag> notRelatedTags = elearnTermBean.updateRandomTagsNotRelated(term, maxNrResults); List<Tag> rejectedTags = term.getRejectedTags(); Collections.shuffle(rejectedTags); tags = new ArrayList<Tag>(); // Add wrong entries int rejectedNr = 0; for (int i = 0; i < maxNrResults; i++) { if (Math.random() > 0.33333 && rejectedNr < rejectedTags.size()) tags.add(rejectedTags.get(rejectedNr++)); else tags.add(notRelatedTags.get(i)); } // Add correct entries List<Tag> confirmedTags = term.getConfirmedTags(); Collections.shuffle(confirmedTags); for (int i = 0; i < maxNrResults; i++) { tags.add(confirmedTags.get(i)); } Collections.shuffle(tags); // 2. Create store for the user's selection answers = new ArrayList<String>(); } /** * Action for normal game round (not for palette mode) */ public void choose() { if (association == null || association.length() == 0) { facesMessages.addFromResourceBundle("termina.term.tooShort"); return; } if (TagSemantics.containsNotNormalized(previousTaggings, association) != null) { facesMessages.addFromResourceBundle("termina.term.duplicate"); lastAssociation = new MatchingTag("duplicate"); log.info("Association #0 has already been said for term #1", association, term); return; } Tagging tagging = new Tagging(); initializeAction(tagging); tagging.setResource(term); Tag tag = TerminaMatching.checkAssociationInList(association, term.getConfirmedTags()); lastAssociation = new MatchingTag(association); if (tag != null) { log.info("Association '#0' is correct for term '#1'", association, term); facesMessages.addFromResourceBundle("termina.term.correct"); tagging.setTag(tag); foundAssociations++; tagging.setScore(scoreMultiplicator()); currentRoundScore += scoreMultiplicator(); lastAssociation.setDirectMatch(true); lastAssociation.setScore(tagging.getScore()); } else { tag = TerminaMatching.checkAssociationInList(association, term.getRejectedTags()); if (tag != null) { log.info("Association '#0' is wrong for term '#1'", association, term); facesMessages.addFromResourceBundle("termina.term.wrong"); tagging.setTag(tag); tagging.setScore(WRONG_ASSOCIATION_SCORE); currentRoundScore += WRONG_ASSOCIATION_SCORE; lastAssociation.setScore(tagging.getScore()); } else { log.info("Association '#0' is unknown for term '#1'", association, term); facesMessages.addFromResourceBundle("termina.term.unknown"); tagging.setTag(findOrCreateTag(association)); lastAssociation.setIndirectMatch(true); } } previousTaggings.add(tagging.getTag()); entityManager.persist(tagging); gameRound.getActions().add(tagging); association = ""; } @Override public void endRound() { this.paletteMode = false; currentRoundScore -= scoreMultiplicator() * (gameConfiguration.getBid() - foundAssociations); super.endRound(); entityManager.flush(); // if (getRoundsLeft().equals(0)) { // nextGameConfiguration.setLevel(gameConfiguration.getLevel()+1); // log.info("Next level: #0", nextGameConfiguration.getLevel()); // } } public String choosePalette() { foundAssociations = 0; for (String tag : answers) { Tagging tagging = new Tagging(); initializeAction(tagging); tagging.setResource(term); Tag tagT = findOrCreateTag(tag); tagging.setTag(tagT); if (term.getConfirmedTags().contains(tagT)) { foundAssociations++; tagging.setScore(scoreMultiplicator()); } else { foundAssociations--; tagging.setScore(-scoreMultiplicator()); } entityManager.persist(tagging); gameRound.getActions().add(tagging); log.info("Chose tag #0", tagT); } if (foundAssociations > 0) { currentRoundScore += foundAssociations*scoreMultiplicator(); } else { foundAssociations = 0; } if (foundAssociations == gameConfiguration.getBid()) facesMessages.addFromResourceBundle("termina.term.correct"); return "next"; } private int scoreMultiplicator() { int zeitbonus = 1; if (gameConfiguration.getRoundDuration() <= 15) { zeitbonus = 4; } else if (gameConfiguration.getRoundDuration() <= 30) { zeitbonus = 3; } else if (gameConfiguration.getRoundDuration() <= 45) { zeitbonus = 2; } return zeitbonus * term.getRating(); } @Override public Integer getRoundsLeft() { if (elearnTermBean.updateTerm(nextGameConfiguration) != null) return 1; else return 0; } /** * @return returns an appropriate messages that shows players how good they were */ public String getScoringText() { if (getScore() <= 0 || (getRoundsLeft() > 0 && getScore() < roundNr)) { return messages.get("scoring.notGood"); } else { // calculcate highscore HighscoreBean highscoreBean = (HighscoreBean) Component.getInstance(HighscoreBean.class); List<HighscoreSet> highscores = highscoreBean.getHighscores(); for (HighscoreSet highscoreSet : highscores) { if (highscoreSet.getGameType().getName().equals("elearnTermina")) { List<Highscore> highscore = highscoreSet.getHighscoreAll(); if (highscore.size() > 0 && (highscore.get(0).getPersonId().equals(person.getId()) || (person.getPersonConnected() != null && highscore.get(0).getPersonId().equals(person.getPersonConnected().getId())))) { return messages.get("scoring.goodHighscore"); } break; } } // check if player chose maximum difficulty if (gameConfiguration.getBid() < 5 || gameConfiguration.getRoundDuration() > 15) return messages.get("scoring.goodNotDifficult"); else return messages.get("scoring.goodDifficult"); } } public void riseBid() { if (nextGameConfiguration.getBid() < 5) nextGameConfiguration.setBid(nextGameConfiguration.getBid() + 1); } public void lowerBid() { if (nextGameConfiguration.getBid() > 1) nextGameConfiguration.setBid(nextGameConfiguration.getBid() - 1); } public void riseRoundDuration() { if (nextGameConfiguration.getRoundDuration() < 60) nextGameConfiguration.setRoundDuration(nextGameConfiguration.getRoundDuration() + 15); } public void lowerRoundDuration() { if (nextGameConfiguration.getRoundDuration() > 15) nextGameConfiguration.setRoundDuration(nextGameConfiguration.getRoundDuration() - 15); } public GameConfiguration getGameConfiguration() { return gameConfiguration; } public GameConfiguration getNextGameConfiguration() { return nextGameConfiguration; } public String getAssociation() { return association; } public void setAssociation(String association) { this.association = association; } public Integer getFoundAssociations() { return foundAssociations; } public List<Tag> getTags() { return tags; } public List<String> getAnswers() { return answers; } public void setAnswers(List<String> answers) { this.answers = answers; } public Term getTerm(){ return this.term; } /** * Important: Normalize the tag first, e.g. with TagSemantics.normalize() * * @param tagName * @return */ public Tag findOrCreateTag(String tagName) { if (tagName.length() > 0) { String language = localeSelector.getLanguage(); log.info("Added '#0' to tags.", tagName); Query query = entityManager.createNamedQuery("tag.tagByNameAndLanguage"); query.setParameter("language", language); query.setParameter("name", tagName); Tag tag; try { tag = (Tag) query.getSingleResult(); } catch (NonUniqueResultException e) { log.error("The tag #0 (#1) is not unique", tagName, language); @SuppressWarnings("unchecked") List<Tag> tagList = query.getResultList(); tag = tagList.get(0); } catch(NoResultException e) { log.info("The tag #0 (#1) is new", tagName, language); tag = new Tag(); tag.setName(tagName); tag.setLanguage(language); entityManager.persist(tag); } return tag; } else return null; } public MatchingTag getLastAssociation() { return lastAssociation; } public void setLastAssociation(MatchingTag lastAssociation) { this.lastAssociation = lastAssociation; } }