diff --git a/src/main/scala/org/xcit/nback/generators/SkewedSequenceGenerator.scala b/src/main/scala/org/xcit/nback/generators/SkewedSequenceGenerator.scala index c30d341..91f7d26 100644 --- a/src/main/scala/org/xcit/nback/generators/SkewedSequenceGenerator.scala +++ b/src/main/scala/org/xcit/nback/generators/SkewedSequenceGenerator.scala @@ -1,5 +1,6 @@ package org.xcit.nback.generators +import scala.collection.mutable.ListBuffer import scala.util.Random /** @@ -12,50 +13,77 @@ * @param D Probability of showing a non-matching distractor in a trial (from 0 to 100). * @param L1 Probability of showing a matching item with n-1 item (pre-trial match), in a trial (from 0 to 100). * @param L2 Probability of showing a matching item with n+1 item (post-trial match) in a trial (from 0 to 100). - * @param trials Number of trials (length of the generated string). */ class SkewedSequenceGenerator( - alphabet: String = "ABCDEFGH", + alphabet: String = "ABCDEFGH", // it's also called pool in ralph2014 target: Char = 'A', N: Int = 3, - T: Double = 24, - D: Double = 32, - L1: Double = 4, - L2: Double = 4) + T: Int = 24, + D: Int = 32, + L1: Int = 4, + L2: Int = 4) extends SequenceGenerator { + + val trials = T + D + L1 + L2 + /** * Types of each trial (L1, L2, T, or D). */ private object TrialType extends Enumeration { val LURE_BEFORE_TARGET, LURE_AFTER_TARGET, TARGET, DISTRACTOR = Value } - override def generate(trials: Int): String = { - //1. Init alphabet and buffer + + /** + * Ignores trials. Trials' value is defined by N+T+D+L1+L2 + * @param t number of items in n-back sequence. + * @return string of size "trials" with random alphanumeric characters. + */ + override def generate(t: Int): String = { + // 1. Init buffer with empty list of items :-). Why am I explaining this? + var buffer = ListBuffer[String]() var trial = 0 + while (trial < trials) { trial += 1 - //2. For each trial, generate a random type (L1, L2, T, or D) - val trialType = nextTrialType(trials) - } - //3. Based on the generated type, generate the item and append it to the buffer - //4. Convert buffer to string and return it as result - ??? - } + // 2. For each trial, generate a random type (L1, L2, T, or D) + // 3. Based on the generated type, generate the item and append it to the buffer + buffer += {nextTrialType() match { + case TrialType.LURE_BEFORE_TARGET if buffer.length > N + 1 => + buffer(buffer.length - 1 - N -1) + case TrialType.TARGET if buffer.length > N => + buffer(buffer.length - 1 - N) + case TrialType.LURE_BEFORE_TARGET if buffer.length > N => + buffer(buffer.length - N) + case _ => + alphabet.charAt(Random.nextInt(alphabet.length)).toString + }} + + } + // 4. Convert buffer to string and return it as result + buffer.mkString("") + } /** * Defines next trial types randomly. It returns L1, L2, T, or D (Ralph2014). * Random range: [1, trials] (both are inclusive based on ralph2014!) - * @param trials number trials - * @return + * @return a trial type to be used to generate an item for current trial */ - private def nextTrialType(trials: Int): TrialType.Value = { Random.nextInt(trials + 1) + 1 } match { - case rnd if rnd <= L1 => TrialType.LURE_BEFORE_TARGET - case rnd if rnd < rnd && rnd <= L1+L2 => TrialType.LURE_AFTER_TARGET - case rnd if rnd <= L1 => TrialType.TARGET - case _ => TrialType.DISTRACTOR + private def nextTrialType(): TrialType.Value = { Random.nextInt(trials + 1) + 1 } match { + case rnd if rnd <= L1 => + L1 -= 1 + TrialType.LURE_BEFORE_TARGET + case rnd if rnd < rnd && rnd <= L1 + L2 => + L2 -= 1 + TrialType.LURE_AFTER_TARGET + case rnd if rnd <= L1 => + T -= 1 + TrialType.TARGET + case _ => + D -= 1 + TrialType.DISTRACTOR } } diff --git a/src/main/scala/org/xcit/nback/generators/SkewedSequenceGenerator.scala b/src/main/scala/org/xcit/nback/generators/SkewedSequenceGenerator.scala index c30d341..91f7d26 100644 --- a/src/main/scala/org/xcit/nback/generators/SkewedSequenceGenerator.scala +++ b/src/main/scala/org/xcit/nback/generators/SkewedSequenceGenerator.scala @@ -1,5 +1,6 @@ package org.xcit.nback.generators +import scala.collection.mutable.ListBuffer import scala.util.Random /** @@ -12,50 +13,77 @@ * @param D Probability of showing a non-matching distractor in a trial (from 0 to 100). * @param L1 Probability of showing a matching item with n-1 item (pre-trial match), in a trial (from 0 to 100). * @param L2 Probability of showing a matching item with n+1 item (post-trial match) in a trial (from 0 to 100). - * @param trials Number of trials (length of the generated string). */ class SkewedSequenceGenerator( - alphabet: String = "ABCDEFGH", + alphabet: String = "ABCDEFGH", // it's also called pool in ralph2014 target: Char = 'A', N: Int = 3, - T: Double = 24, - D: Double = 32, - L1: Double = 4, - L2: Double = 4) + T: Int = 24, + D: Int = 32, + L1: Int = 4, + L2: Int = 4) extends SequenceGenerator { + + val trials = T + D + L1 + L2 + /** * Types of each trial (L1, L2, T, or D). */ private object TrialType extends Enumeration { val LURE_BEFORE_TARGET, LURE_AFTER_TARGET, TARGET, DISTRACTOR = Value } - override def generate(trials: Int): String = { - //1. Init alphabet and buffer + + /** + * Ignores trials. Trials' value is defined by N+T+D+L1+L2 + * @param t number of items in n-back sequence. + * @return string of size "trials" with random alphanumeric characters. + */ + override def generate(t: Int): String = { + // 1. Init buffer with empty list of items :-). Why am I explaining this? + var buffer = ListBuffer[String]() var trial = 0 + while (trial < trials) { trial += 1 - //2. For each trial, generate a random type (L1, L2, T, or D) - val trialType = nextTrialType(trials) - } - //3. Based on the generated type, generate the item and append it to the buffer - //4. Convert buffer to string and return it as result - ??? - } + // 2. For each trial, generate a random type (L1, L2, T, or D) + // 3. Based on the generated type, generate the item and append it to the buffer + buffer += {nextTrialType() match { + case TrialType.LURE_BEFORE_TARGET if buffer.length > N + 1 => + buffer(buffer.length - 1 - N -1) + case TrialType.TARGET if buffer.length > N => + buffer(buffer.length - 1 - N) + case TrialType.LURE_BEFORE_TARGET if buffer.length > N => + buffer(buffer.length - N) + case _ => + alphabet.charAt(Random.nextInt(alphabet.length)).toString + }} + + } + // 4. Convert buffer to string and return it as result + buffer.mkString("") + } /** * Defines next trial types randomly. It returns L1, L2, T, or D (Ralph2014). * Random range: [1, trials] (both are inclusive based on ralph2014!) - * @param trials number trials - * @return + * @return a trial type to be used to generate an item for current trial */ - private def nextTrialType(trials: Int): TrialType.Value = { Random.nextInt(trials + 1) + 1 } match { - case rnd if rnd <= L1 => TrialType.LURE_BEFORE_TARGET - case rnd if rnd < rnd && rnd <= L1+L2 => TrialType.LURE_AFTER_TARGET - case rnd if rnd <= L1 => TrialType.TARGET - case _ => TrialType.DISTRACTOR + private def nextTrialType(): TrialType.Value = { Random.nextInt(trials + 1) + 1 } match { + case rnd if rnd <= L1 => + L1 -= 1 + TrialType.LURE_BEFORE_TARGET + case rnd if rnd < rnd && rnd <= L1 + L2 => + L2 -= 1 + TrialType.LURE_AFTER_TARGET + case rnd if rnd <= L1 => + T -= 1 + TrialType.TARGET + case _ => + D -= 1 + TrialType.DISTRACTOR } } diff --git a/src/main/scala/org/xcit/nback/markov/models/State.scala b/src/main/scala/org/xcit/nback/markov/models/State.scala index 7a0bfba..ab94bdb 100644 --- a/src/main/scala/org/xcit/nback/markov/models/State.scala +++ b/src/main/scala/org/xcit/nback/markov/models/State.scala @@ -4,14 +4,12 @@ /** * A Single state with a label in Markov chain - * @param lbl + * @param label A simple string label, representing the state and node */ -class State(lbl: String) { +class State(label: String) { val transitions: mutable.HashMap[State, Transition] = mutable.HashMap[State, Transition]() - def label: String = lbl - //TODO move to chain def probability(): Double = transitions.size.toDouble / MarkovChain.totalTransitions().toDouble }