1 // -*- Mode: C++; tab-width:2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2 // vi:tw=80:et:ts=2:sts=2
3 //
4 // -----------------------------------------------------------------------
5 //
6 // This file is part of RLVM, a RealLive virtual machine clone.
7 //
8 // -----------------------------------------------------------------------
9 //
10 // Copyright (C) 2013 Elliot Glaysher
11 //
12 // This program is free software; you can redistribute it and/or modify
13 // it under the terms of the GNU General Public License as published by
14 // the Free Software Foundation; either version 3 of the License, or
15 // (at your option) any later version.
16 //
17 // This program is distributed in the hope that it will be useful,
18 // but WITHOUT ANY WARRANTY; without even the implied warranty of
19 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20 // GNU General Public License for more details.
21 //
22 // You should have received a copy of the GNU General Public License
23 // along with this program; if not, write to the Free Software
24 // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
25 // -----------------------------------------------------------------------
26 
27 #include "modules/module_sys_timetable2.h"
28 
29 #include <cmath>
30 
31 #include "machine/rlmachine.h"
32 #include "machine/rlmodule.h"
33 #include "utilities/exception.h"
34 #include "utilities/math_util.h"
35 
36 // static
GetTypeForTag(const libreallive::ExpressionPiece & sp)37 int TimeTableMapper::GetTypeForTag(const libreallive::ExpressionPiece& sp) {
38   switch (sp.GetOverloadTag()) {
39     case 48:
40       return 0;
41     case 65584:
42       return 1;
43     case 49:
44       return 2;
45     case 50:
46       return 3;
47     case 51:
48       return 4;
49     case 52:
50       return 5;
51     case 53:
52       return 6;
53     case 54:
54       return 7;
55     case 55:
56       return 8;
57     default: {
58       std::ostringstream oss;
59       oss << "Invalid timetable2 tag: " << sp.GetOverloadTag();
60       throw rlvm::Exception(oss.str());
61     }
62   }
63 }
64 
65 // -----------------------------------------------------------------------
66 
operator ()(RLMachine & machine,int now_time,int rep_time,int start_time,int start_num,TimeTable2List::type index_list)67 int Sys_timetable2::operator()(RLMachine& machine,
68                                int now_time,
69                                int rep_time,
70                                int start_time,
71                                int start_num,
72                                TimeTable2List::type index_list) {
73   now_time = now_time + rep_time;
74 
75   if (start_time > now_time)
76     return start_num;
77 
78   int value = start_num;
79 
80   for (TimeTable2List::type::iterator it = index_list.begin();
81        it != index_list.end();
82        ++it) {
83     switch (it->type) {
84       case 0: {
85         int end_time = std::get<0>(it->first);
86         int end_num = std::get<1>(it->first);
87         if (now_time > start_time && now_time <= end_time) {
88           return InterpolateBetween(start_time, now_time, end_time,
89                                     value, end_num, 0);
90         } else {
91           value = end_num;
92         }
93 
94         start_time = end_time;
95         break;
96       }
97       case 1: {
98         int end_time = std::get<0>(it->second);
99         int end_num = std::get<1>(it->second);
100         int mod = std::get<2>(it->second);
101 
102         if (now_time > start_time && now_time <= end_time) {
103           return InterpolateBetween(start_time, now_time, end_time,
104                                     value, end_num, mod);
105         } else {
106           value = end_num;
107         }
108 
109         start_time = end_time;
110         break;
111       }
112       case 2: {
113         value = it->third;
114         break;
115       }
116       case 3: {
117         int end_time = it->fourth;
118         if (now_time > start_time && now_time <= end_time) {
119           return value;
120         }
121 
122         start_time = end_time;
123         break;
124       }
125       case 7: {
126         int end_time = std::get<0>(it->eighth);
127         int end_num = std::get<1>(it->eighth);
128         if (now_time > start_time && now_time <= end_time) {
129           int count = std::get<2>(it->eighth);
130           return Jump(start_time, now_time, end_time, value, end_num, count);
131         } else {
132           value = end_num;
133         }
134 
135         // Finally, set the start time to our end time.
136         start_time = end_time;
137         break;
138       }
139       case 8: {
140         int end_time = std::get<0>(it->ninth);
141         int end_num = std::get<1>(it->ninth);
142 
143         if (now_time > start_time && now_time <= end_time) {
144           return value;
145         } else {
146           value = end_num;
147         }
148 
149         start_time = end_time;
150         break;
151       }
152       default: {
153         std::ostringstream oss;
154         oss << "We don't handle " << it->type << " yet.";
155         throw rlvm::Exception(oss.str());
156       }
157     }
158   }
159 
160   return value;
161 }
162 
Jump(int start_time,int now_time,int end_time,int start_num,int end_num,int count)163 int Sys_timetable2::Jump(int start_time,
164                          int now_time,
165                          int end_time,
166                          int start_num,
167                          int end_num,
168                          int count) {
169   int duration = end_time - start_time;
170   int one_cycle = duration / count;
171 
172   int current_time = now_time - start_time;
173   int current_cycle = current_time % one_cycle;
174 
175   double percent = static_cast<double>(current_cycle) / one_cycle;
176   return start_num +
177          ((end_num - start_num) * (1 - std::pow(percent * 2 - 1, 2)));
178 }
179 
180 // -----------------------------------------------------------------------
181 
182 struct Sys_timetablelen2 : public Sys_timetable2 {
operator ()Sys_timetablelen2183   virtual int operator()(RLMachine& machine,
184                          int now_time,
185                          int rep_time,
186                          int start_time,
187                          int start_num,
188                          TimeTable2List::type index_list) {
189     // Modify each part of the index list to keep a running sum of times. This
190     // changes a list of durations into a list of absolute times.
191     int total = 0;
192 
193     for (TimeTable2List::type::iterator it = index_list.begin();
194          it != index_list.end();
195          ++it) {
196       switch (it->type) {
197         case 0: {
198           total += std::get<0>(it->first);
199           std::get<0>(it->first) = total;
200           break;
201         }
202         case 1: {
203           total += std::get<0>(it->second);
204           std::get<0>(it->second) = total;
205           break;
206         }
207         case 2: {
208           // Set has no time, therefore it isn't modified.
209           break;
210         }
211         case 3: {
212           total += it->fourth;
213           it->fourth = total;
214           break;
215         }
216         case 7: {
217           total += std::get<0>(it->eighth);
218           std::get<0>(it->eighth) = total;
219           break;
220         }
221         case 8: {
222           total += std::get<0>(it->ninth);
223           std::get<0>(it->ninth) = total;
224           break;
225         }
226         default: {
227           std::ostringstream oss;
228           oss << "We don't handle " << it->type << " yet.";
229           throw rlvm::Exception(oss.str());
230         }
231       }
232     }
233 
234     return Sys_timetable2::operator()(
235         machine, now_time, rep_time, start_time, start_num, index_list);
236   }
237 };
238 
239 // -----------------------------------------------------------------------
240 
AddTimetable2Opcode(RLModule & module)241 void AddTimetable2Opcode(RLModule& module) {
242   module.AddOpcode(810, 0, "timetable2", new Sys_timetable2);
243   module.AddOpcode(811, 0, "timetablelen2", new Sys_timetablelen2);
244 }
245