"""RESS.py Random Equal Stimulus Sets Originally coded in Object Pascal for Delphi by Wesley R. Elsberry around 1999. Translation to Python 3 by ChatGPT (GPT-4) 2023-06-01. Random Equal Stimulus Sets are sequences of numbers indicating one of a set of stimuli to be presented to a subject in a cognitive or psychophysics task. The basic rules for generating these sequences is derived from Gellermann 1925(?), but modified to permit the specification of more than two stimuli in the set. The restriction on a maximum of three sequential presentations of the same stimulus is retained. Issues: The 'next_yield' method does not work. Using 'next' for a sequence longer than the defined length of sequence can cause there to be sequences that violate Gellermann's assumptions, as the sequences composed together are not tested across the joins. """ import sys import os import traceback import random MAXRESS = 120 # Arbitrary maximum class RESS: """ RESS class represents the equivalent of the Pascal unit 'ress' in Python. Random Equal Stimulus Sets are sequences of numbers indicating one of a set of stimuli to be presented to a subject in a cognitive or psychophysics task. The basic rules for generating these sequences is derived from Gellermann 1925(?), but modified to permit the specification of more than two stimuli in the set. The restriction on a maximum of three sequential presentations of the same stimulus is retained. """ def __init__(self): self.classes = None self.thelength = None self.series = [0] * MAXRESS self.lastseries = [0] * MAXRESS self.cnt = None self.seriesstr = "" self.current = None self.dummy = None self.hist = [0] * 61 def init(self): """ Initializes the variables in TRESS. """ self.classes = 1 self.thelength = 0 self.series = [0] * MAXRESS self.lastseries = [0] * MAXRESS self.hist = [0] * 61 self.cnt = 0 self.seriesstr = "" self.dummy = 0 def makestring(self): """ Creates a string representation of the series. Returns: The string representation of the series. """ tstr = "" for val in self.series[1:self.thelength + 1]: tstr += str(val) self.seriesstr = tstr return tstr def generate(self, len, nclass): """ Generates a candidate series. Args: len: The length of the series. nclass: The number of classes. """ self.cnt = 0 self.classes = nclass # Constraint: sequence length less than maximum if MAXRESS >= len: self.thelength = len else: self.thelength = MAXRESS # Constraint: Multiple of number of classes if self.thelength % self.classes != 0: self.thelength -= self.thelength % self.classes for i in range(self.classes): self.hist[i] = self.thelength // self.classes self.series[0] = random.randint(0, self.classes - 1) self.hist[self.series[0]] -= 1 run = 1 for i in range(1, self.thelength): ctr = 0 while True: ctr += 1 jj = random.randint(0, self.classes - 1) if self.hist[jj] > 0: shortrun = (self.series[i - 1] == jj and run < 3) or (self.series[i - 1] != jj) break if ctr > 100: break if self.series[i - 1] == jj: run += 1 else: run = 1 self.hist[jj] -= 1 self.series[i] = jj def test(self): """ Tests candidates for criteria. Returns: True if the series is valid, False otherwise. """ ok = True hist = [0] * 61 for val in self.series[:self.thelength]: hist[val] += 1 for i in range(self.classes - 1): if hist[i] != hist[i + 1]: ok = False if ok: run = 1 for i in range(1, self.thelength): if self.series[i - 1] == self.series[i]: run += 1 if run > 3: ok = False else: run = 1 return ok def newress(self, nlen=24, nclass=2): """ Finds and saves a valid series using generate and test. Args: nlen: The length of the series. nclass: The number of classes. """ print('nlen', nlen, 'nclass', nclass) try: random.seed() self.lastseries = self.series while True: self.generate(nlen, nclass) # print("gen", self.makestring()) if self.test(): break return self.makestring() except: estr = f"Error: {traceback.format_exc()}" print(estr) return '' def next(self): """ Returns the next value within a series. Returns: The next value in the series. """ if self.cnt >= self.thelength: self.newress(self.thelength, self.classes) self.cnt += 1 self.current = self.series[self.cnt] return self.series[self.cnt] def next_yield(self): """ Yields the next value within a series. """ print('start', self.series, self.cnt, self.series[self.cnt]) while True: if self.cnt >= self.thelength: print("calling newress") self.newress(self.thelength, self.classes) self.cnt = 0 print(self.cnt) print(self.series, self.cnt, self.series[self.cnt]) self.current = self.series[self.cnt] yield str(self.current) self.cnt += 1 # Exercise the TRESS code from random import seed def main(): # Set the seed for random number generation seed() # Create an instance of the TRESS class ress1 = RESS() # Initialize the TRESS object ress1.init() # Generate and print a valid series ress1.newress(24, 3) series = ress1.makestring() print("Generated Series:", series) ress1.newress(24, 3) series = ress1.makestring() print("Generated Series:", series) ress1.newress(24, 3) series = ress1.makestring() print("Generated Series:", series) ress1.newress(24, 3) series = ress1.makestring() print("Generated Series:", series) ress1.newress(24, 3) series = ress1.makestring() print("Generated Series:", series) # Generate and print the next value in the series for ii in range(26): next_val = ress1.next() print(ii, "Next Value:", str(next_val)) if __name__ == "__main__": main()