2
* Copyright © 2012 Keith Packard <keithp@keithp.com>
4
* This program is free software; you can redistribute it and/or modify
5
* it under the terms of the GNU General Public License as published by
6
* the Free Software Foundation; version 2 of the License.
8
* This program is distributed in the hope that it will be useful, but
9
* WITHOUT ANY WARRANTY; without even the implied warranty of
10
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11
* General Public License for more details.
13
* You should have received a copy of the GNU General Public License along
14
* with this program; if not, write to the Free Software Foundation, Inc.,
15
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
21
autoload Client::Draw;
23
autoload Nichrome::Timer;
25
extend namespace Nichrome {
27
public namespace RRboard {
32
public int board_width = RR::Width * Draw::cell_width;
33
public int board_height = RR::Height * Draw::cell_height;
35
public typedef widget_t + struct {
37
RR::RobotOrNone active_robot;
38
*Timer::timer_t timer;
39
void (RR::Color color,
40
RR::Direction direction) move_callback;
41
int button_x, button_y;
44
real dimension(&rrboard_widget_t widget) = min (widget.geometry.width, widget.geometry.height);
46
Draw::transform_t transform(&rrboard_widget_t widget) {
47
real dim = dimension(&widget);
48
return (Draw::transform_t) {
49
.xscale = dim / board_width,
50
.yscale = dim / board_height,
51
.xoff = (widget.geometry.width - dim) // 2,
52
.yoff = (widget.geometry.height - dim) // 2
56
void draw (cairo_t cr, &rrboard_widget_t widget) {
57
Draw::transform_t t = transform(&widget);
60
for (int y = 0; y < RR::Height; y++)
61
for (int x = 0; x < RR::Width; x++) {
62
Draw::background(cr, x, y, widget.board[x,y], &t);
63
Draw::walls(cr, x, y, widget.board[x,y], &t);
64
Draw::contents(cr, x, y, widget.board[x,y], widget.active_robot, &t);
66
for (int y = RR::Height / 2 - 1; y < RR::Height/2 + 1; y++)
67
for (int x = RR::Width / 2 - 1; x < RR::Width/2 + 1; x++) {
68
Draw::background(cr, x, y, widget.board[x,y], &t);
69
Draw::walls(cr, x, y, widget.board[x,y], &t);
71
Draw::target(cr, RR::Width / 2 - 1, RR::Height / 2 - 1,
72
RR::active_target(&widget.board), &t);
76
void outline (cairo_t cr, &rrboard_widget_t widget) {
77
rectangle(cr, 0, 0, widget.geometry.width, widget.geometry.height);
80
void natural (cairo_t cr, &rrboard_widget_t widget) {
81
rectangle(cr, 0, 0, board_width, board_height);
84
/* Override default widget configure function to also reposition
87
void configure (&rrboard_widget_t widget, rect_t geometry) {
88
Widget::configure(&widget, geometry);
90
/* Configure timer to sit over the central
93
real board_dim = dimension(&widget);
94
real timer_dim = board_dim * 2 / RR::Width;
95
real timer_pos = board_dim / RR::Width * 7;
96
widget.timer->configure (widget.timer,
98
.x = geometry.x + timer_pos,
99
.y = geometry.y + timer_pos,
105
void set_active_robot(&rrboard_widget_t widget, RR::Robot robot) {
106
widget.active_robot = (RR::RobotOrNone.robot) robot;
107
Widget::redraw(&widget);
110
void set_active (&rrboard_widget_t widget, string color) {
112
set_active_robot(&widget, (RR::Robot) { .color = RR::color(color) });
113
} catch RR::rr_error(RR::Error error) {
117
void move_active (&rrboard_widget_t widget, string dir) {
119
RR::Direction direction = RR::direction(dir);
120
union switch (widget.active_robot) {
122
widget.move_callback(r.color, direction);
126
} catch RR::rr_error(RR::Error error) {
130
protected void key (&rrboard_widget_t widget, &key_event_t event) {
132
if (event.type != key_type_t.press)
140
set_active (&widget, event.key);
143
set_active (&widget, "whirl");
145
case "Left": case "w": case "W":
146
move_active(&widget, "west");
148
case "Right": case "e": case "E":
149
move_active(&widget, "east");
151
case "Up": case "n": case "N":
152
move_active(&widget, "north");
154
case "Down": case "s": case "S":
155
move_active(&widget, "south");
160
typedef struct { int x, y; } position_t;
163
* A bit expensive, but it's more reliable than trying to
164
* keep track of robot positions separately
166
position_t find_robot (&rrboard_widget_t widget, RR::Robot robot) {
167
for (int y = 0; y < RR::Height; y++)
168
for (int x = 0; x < RR::Width; x++) {
169
union switch (widget.board[x,y].robot) {
171
if (r.color == robot.color)
172
return (position_t) { .x = x, .y = y };
177
return (position_t) { .x = 0, .y = 0 };
180
protected void button (&rrboard_widget_t widget, &button_event_t event) {
182
/* Convert button position to board location */
183
Draw::transform_t t = transform(&widget);
184
int x = floor ((event.x - t.xoff) / t.xscale / Draw::cell_width);
185
int y = floor ((event.y - t.yoff) / t.yscale / Draw::cell_height);
187
enum switch (event.type) {
189
RR::Object object = widget.board[x,y];
191
/* Clicking on a robot selects that robot
193
union switch (object.robot) {
195
set_active_robot(&widget, r);
202
/* Releasing with an active robot moves the robot
203
* towards the point of release
205
union switch (widget.active_robot) {
207
position_t robot_pos = find_robot(&widget, r);
208
int dx = x - robot_pos.x;
209
int dy = y - robot_pos.y;
211
if (abs (dx) > abs (dy)) {
213
move_active(&widget, "west");
215
move_active(&widget, "east");
218
move_active(&widget, "north");
220
move_active(&widget, "south");
231
protected void set_timer (&rrboard_widget_t widget, real time) {
232
Timer::set_timer(widget.timer, time);
235
protected void stop_timer (&rrboard_widget_t widget) {
236
Timer::stop_timer(widget.timer);
239
public *rrboard_widget_t new(&nichrome_t nichrome,
240
void(RR::Color color, RR::Direction dir) move_callback){
241
&rrboard_widget_t widget = &(rrboard_widget_t) {};
243
widget.timer = Timer::new(&nichrome); /* make sure timer is above rrboard */
244
Widget::init(&nichrome, &widget);
246
widget.outline = outline;
247
widget.natural = natural;
248
widget.configure = configure;
250
widget.button = button;
251
widget.active_robot = RobotOrNone.none;
252
widget.move_callback = move_callback;
253
widget.board = (RR::Board) { { { .robot = RobotOrNone.none,
254
.target = TargetOrNone.none,
255
.walls = { .left = false, .right = false,
256
.above = false, .below = false }