I6 Template Layer

Inform 7 6M62ContentsIntroductionFunction IndexRules Index

Time.i6t

Time contents

Rounding.

The following rounds a numerical value t1 to the nearest unit of t2; for instance, if t2 is 5 then it rounds to the nearest 5. The name is an anachronism, as it's used for all kinds of value.

13[ RoundOffTime t1 t2; 14    if (t1 >= 0) return ((t1+t2/2)/t2)*t2; 15    return -((-t1+t2/2)/t2)*t2; 16];

Conversion To Number.

21[ NUMBER_TY_to_TIME_TY n; 22    n = n%1440; 23    if (n < 0) return n + 1440; 24    return n; 25];

Square Root.

Although this routine performs integer square root, it does so using Glulx's floating-point operations if available (with code contributed by Andrew Plotkin): this is fast and remains accurate up to about 16 million.

The slower integer method is an old algorithm for extracting binary square roots, taking 2 bits at a time. We start out with one at the highest bit which isn't the sign bit; that used to be worked out as WORD_HIGHBIT/2, but this caused unexpected portability problems (exposing a minor bug in Inform and also glulxe) because of differences in how C compilers handle signed division of constants in the case where the dividend is -231, the unique number which cannot be negated in 32-bit twos complement arithmetic.

41[ SquareRoot num 42    op res one n x; 43    if (num < 0) { RunTimeProblem(RTP_NEGATIVEROOT); return 1; } 44 45    ! Use floating-point ops if available. 46    #ifdef TARGET_GLULX; 47    @gestalt 11 0 n; 48    if (n) { 49        @numtof num x; 50        @sqrt x x; 51        @ftonumz x num; 52        return num; 53    } 54   #endif; 55    op = num; 56    if (num < 0) { RunTimeProblem(RTP_NEGATIVEROOT); return 1; } 57    ! "one" starts at the highest power of four <= the argument. 58    for (one = WORD_NEXTTOHIGHBIT: one > op: one = one/4) ; 59 60    while (one ~= 0) { 61        ! print "Round: op = ", op, " res = ", res, ", res**2 = ", res*res, " one = ", one, " nthb = ", WORD_NEXTTOHIGHBIT, "^"; 62        if (op >= res + one) { 63            op = op - res - one; 64            res = res/2 + one; 65        } else { 66            res = res/2; 67        } 68        one = one/4; 69    } 70    ! print "Res is ", res, "^"; 71    return res; 72];

Cube Root.

The following, again, uses floating-point arithmetic if it's available: this is fast and gives good accuracy for smallish numbers, but limited precision begins to tell at around 2000000.

The alternative is an iterative scheme for finding cube roots by Newton-Raphson approximation, not a great method but which, on the narrow ranges of integers we deal with, just about good enough. The square root is used only as a sighting shot.

85[ CubeRoot num neg x y n; 86    ! Use floating-point ops if available. 87    #ifdef TARGET_GLULX; 88    @gestalt 11 0 n; 89    if (n) { 90        if (num < 0) { 91            neg = true; 92            num = -num; 93        } 94        @numtof num x; 95        @pow x 1051372203 x; ! pow(x, 0.3333) 96        @ftonumz x num; 97        if (neg) 98            return -num; 99        else 100            return num; 101    } 102    #endif; 103    if (num < 0) x = -SquareRoot(-num); else x = SquareRoot(num); 104    for (n=0: (y ~= x) && (n++ < 100): y = x, x = (2*x + num/x/x)/3) ; 105    return x; 106];

Digital Printing.

For instance, "2:06 am".

112[ PrintTimeOfDay t h aop; 113    if (t<0) { print "<no time>"; return; } 114    if (t >= TWELVE_HOURS) { aop = "pm"; t = t - TWELVE_HOURS; } else aop = "am"; 115    h = t/ONE_HOUR; if (h==0) h=12; 116    print h, ":"; 117    if (t%ONE_HOUR < 10) print "0"; print t%ONE_HOUR, " ", (string) aop; 118];

Analogue Printing.

For instance, "six minutes past two".

124[ PrintTimeOfDayEnglish t h m dir aop; 125    h = (t/ONE_HOUR) % 12; m = t%ONE_HOUR; if (h==0) h=12; 126    if (m==0) { print (number) h, " oclock"; return; } 127    dir = "past"; 128    if (m > HALF_HOUR) { m = ONE_HOUR-m; h = (h+1)%12; if (h==0) h=12; dir = "to"; } 129    switch(m) { 130        QUARTER_HOUR: print "quarter"; HALF_HOUR: print "half"; 131        default: print (number) m; 132            if (m%5 ~= 0) { 133                if (m == 1) print " minute"; else print " minutes"; 134            } 135    } 136    print " ", (string) dir, " ", (number) h; 137];

Understanding.

This I6 grammar token converts words in the player's command to a valid I7 time, and is heavily based on the one presented as a solution to an exercise in the DM4.

145[ TIME_TOKEN first_word second_word at length flag 146    illegal_char offhour hr mn i original_wn; 147    original_wn = wn; 148{-call:PL::Parsing::Tokens::Values::time} 149    wn = original_wn; 150    first_word = NextWordStopped(); 151    switch (first_word) { 152        'midnight': parsed_number = 0; return GPR_NUMBER; 153        'midday', 'noon': parsed_number = TWELVE_HOURS; 154        return GPR_NUMBER; 155    } 156    ! Next try the format 12:02 157    at = WordAddress(wn-1); length = WordLength(wn-1); 158    for (i=0: i<length: i++) { 159        switch (at->i) { 160            ':': if (flag == false && i>0 && i<length-1) flag = true; 161            else illegal_char = true; 162            '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': ; 163            default: illegal_char = true; 164        } 165    } 166    if (length < 3 || length > 5 || illegal_char) flag = false; 167    if (flag) { 168        for (i=0: at->i~=':': i++, hr=hr*10) hr = hr + at->i - '0'; 169        hr = hr/10; 170        for (i++: i<length: i++, mn=mn*10) mn = mn + at->i - '0'; 171        mn = mn/10; 172        second_word = NextWordStopped(); 173        parsed_number = HoursMinsWordToTime(hr, mn, second_word); 174        if (parsed_number == -1) return GPR_FAIL; 175        if (second_word ~= 'pm' or 'am') wn--; 176        return GPR_NUMBER; 177    } 178    ! Lastly the wordy format 179    offhour = -1; 180    if (first_word == 'half') offhour = HALF_HOUR; 181    if (first_word == 'quarter') offhour = QUARTER_HOUR; 182    if (offhour < 0) offhour = TryNumber(wn-1); 183    if (offhour < 0 || offhour >= ONE_HOUR) return GPR_FAIL; 184    second_word = NextWordStopped(); 185    switch (second_word) { 186        ! "six o'clock", "six" 187        'o^clock', 'am', 'pm', -1: 188            hr = offhour; if (hr > 12) return GPR_FAIL; 189        ! "quarter to six", "twenty past midnight" 190        'to', 'past': 191            mn = offhour; hr = TryNumber(wn); 192            if (hr <= 0) { 193                switch (NextWordStopped()) { 194                    'noon', 'midday': hr = 12; 195                    'midnight': hr = 0; 196                    default: return GPR_FAIL; 197                } 198            } 199            if (hr >= 13) return GPR_FAIL; 200            if (second_word == 'to') { 201                mn = ONE_HOUR-mn; hr--; if (hr<0) hr=23; 202            } 203            wn++; second_word = NextWordStopped(); 204        ! "six thirty" 205        default: 206            hr = offhour; mn = TryNumber(--wn); 207            if (mn < 0 || mn >= ONE_HOUR) return GPR_FAIL; 208            wn++; second_word = NextWordStopped(); 209    } 210    parsed_number = HoursMinsWordToTime(hr, mn, second_word); 211    if (parsed_number < 0) return GPR_FAIL; 212    if (second_word ~= 'pm' or 'am' or 'o^clock') wn--; 213    return GPR_NUMBER; 214]; 215 216[ HoursMinsWordToTime hour minute word x; 217    if (hour >= 24) return -1; 218    if (minute >= ONE_HOUR) return -1; 219    x = hour*ONE_HOUR + minute; if (hour >= 13) return x; 220    x = x % TWELVE_HOURS; if (word == 'pm') x = x + TWELVE_HOURS; 221    if (word ~= 'am' or 'pm' && hour == 12) x = x + TWELVE_HOURS; 222    return x; 223];

Relative Time Token.

"Time" is an interesting kind of value since it can hold two conceptually different ways of thinking about time: absolute times, such as "12:03 PM", and also relative times, like "ten minutes". For parsing purposes, these are completely different from each other, and the time token above handles only absolute times; we need the following for relative ones.

233[ RELATIVE_TIME_TOKEN first_word second_word offhour mult mn original_wn; 234    original_wn = wn; 235    wn = original_wn; 236     237    first_word = NextWordStopped(); wn--; 238    if (first_word == 'an' or 'a//') mn=1; else mn=TryNumber(wn); 239     240    if (mn == -1000) { 241        first_word = NextWordStopped(); 242        if (first_word == 'half') offhour = HALF_HOUR; 243        if (first_word == 'quarter') offhour = QUARTER_HOUR; 244        if (offhour > 0) { 245            second_word = NextWordStopped(); 246            if (second_word == 'of') second_word = NextWordStopped(); 247            if (second_word == 'an') second_word = NextWordStopped(); 248            if (second_word == 'hour') { 249                parsed_number = offhour; 250                return GPR_NUMBER; 251            } 252        } 253        return GPR_FAIL; 254    } 255    wn++; 256     257    first_word = NextWordStopped(); 258    switch (first_word) { 259        'minutes', 'minute': mult = 1; 260        'hours', 'hour': mult = 60; 261        default: return GPR_FAIL; 262    } 263    parsed_number = mn*mult; 264    if (mult == 60) { 265        mn=TryNumber(wn); 266        if (mn ~= -1000) { 267            wn++; 268            first_word = NextWordStopped(); 269            if (first_word == 'minutes' or 'minute') 270                parsed_number = parsed_number + mn; 271            else wn = wn - 2; 272        } 273    } 274    return GPR_NUMBER; 275];

During Scene Matching.

280[ DuringSceneMatching prop sc; 281    for (sc=0: sc<NUMBER_SCENES_CREATED: sc++) 282        if ((scene_status-->sc == 1) && (prop(sc+1))) rtrue; 283    rfalse; 284];

Scene Questions.

289[ SceneUtility sc task; 290    if (sc <= 0) return 0; 291    if (task == 1 or 2) { 292        if (scene_endings-->(sc-1) == 0) return RunTimeProblem(RTP_SCENEHASNTSTARTED, sc); 293    } else { 294        if (scene_endings-->(sc-1) <= 1) return RunTimeProblem(RTP_SCENEHASNTENDED, sc); 295    } 296    switch (task) { 297        1: return (the_time - scene_started-->(sc-1))%(TWENTY_FOUR_HOURS); 298        2: return scene_started-->(sc-1); 299        3: return (the_time - scene_ended-->(sc-1))%(TWENTY_FOUR_HOURS); 300        4: return scene_ended-->(sc-1); 301    } 302];