miércoles, 24 de junio de 2020

Creando aplicaciones web usando Rust y Rocket (Parte 2)

Continuaremos con el ejemplo que generamos en la entrada anterior para entender más el funcionamiento de Rocket. Podemos cambiar el valor "/" del atributo "#[get("/")]" para cambiar el URL / URI que usa nuestro servidor para apuntar a nuestra función. A esto Rocket le llama las rutas y el enrutamiento ("routing"). Este valor es similar a la "/" que se encuentra en la línea del archivo main.rs: "rocket::ignite().mount("/", routes![index]).launch();" ya que este valor también determina la ruta que nos permite acceder a la página.

En el ejemplo de la entrada anterior usamos "#[get("/")]" y "rocket::ignite().mount("/", routes![index]).launch();". El resultado de esto es que directamente al abrir http://localhost:8000 se activa nuestra función. Esta función Rocket la llama "handler" que es nuestra función encargada de manejar toda solicitud que sea enrutada a ella.

¿Cómo podemos hacer para que nuestra función solo se ejecute con el URL / URI http://localhost:8000/hello ? Esto es muy simple sólo cambiamos nuestro valor de "#[get("/")]" a "#[get("/hello")]":

#![feature(proc_macro_hygiene, decl_macro)]

#[macro_use] extern crate rocket;

#[get("/hello")]
fn index() -> &'static str {
    "Hola, mundo! Estoy usando Rocket"
}

fn main() {
    rocket::ignite().mount("/", routes![index]).launch();
}

Probaremos este cambio ejecutando el comando "cargo new". El resultado es que ahora cuando intentamos abrir http://localhost:8000 directamente no podemos y nos regresa un error 404:
Intentando abrir http://localhost:8000 directamente con "#[get("/hello")]" hace que regrese un error
Esto es porque al poner el valor a "#[get("/hello")]" hemos cambiado el enrutamiento. Es decir nuestra funcionalidad ahora está en otra ruta. Por lo anterior si intentamos abrir la ruta correcta que es http://localhost:8000/hello entonces obtenemos el comportamiento de la entrada anterior:
Abriendo http://localhost:8000/hello resulta en el comportamiento que esperamos
Ahora cambiemos también lo que está en el main(). De forma equivalente a lo que acabamos de ver podemos cambiar el valor del main() de "rocket::ignite().mount("/", routes![index]).launch();" a "rocket::ignite().mount("/api", routes![index]).launch();". Como es de esperarse, el resultado es un error si intentamos abrir http://localhost:8000 o http://localhost:8000/hello:

Intentamos abrir http://localhost:8000 o http://localhost:8000/hello 
También tenemos un error si intentamos abrir http://localhost:8000/api :
Intentamos abrir http://localhost:8000/api pero nos da el mismo mensaje de error
Es decir, ahora si queremos que se ejecute index() tenemos que usar exactamente http://localhost:8000/api/hello :
Abrimos http://localhost:8000/api/hello y funciona como esperamos
Con esto Rocket puede limitar el acceso a las funcionalidades y cuidar que solo se ejecuten las funciones encargadas de manejar las operaciones que requerimos.

Una variación que no mostré aquí es dejando "/" en el get, pero cambiando "rocket::ignite().mount("/", routes![index]).launch();" a "rocket::ignite().mount("/api", routes![index]).launch();". En este punto después de ver todo lo anterior es fácil predecir lo que va a pasar. Si realizamos este cambio el comportamiento será que todas las direcciones "http://localhost:8000", "http://localhost:8000/hello" y "http://localhost:8000/api/hello" serán inválidas, y la correcta ahora será http://localhost:8000/api :
Si realizamos el cambio descrito anteriormente ahora nuestra funcionalidad será accesible en http://localhost:8000/api
De las estructuras anteriores la más extensible es dejar que sea accesible usando http://localhost:8000/api/hello para dejar abierta la posibilidad de extender nuestra API agregando nuevas funciones, por ejemplo podemos agregar una función que imprima un mensaje de despedida usando: http://localhost:8000/api/goodbye .

Fuentes:

lunes, 22 de junio de 2020

Creando aplicaciones web usando Rust y Rocket (Parte 1)

Los resultados de la encuesta de Rust de 2019 reflejan que la mayoría de los que trabajan con Rust en tiempo completo son programadores / ingenieros en software que trabajan en "backend web applications" que son simplemente aplicaciones web para servicios de fondo. Estos servicios de fondo se encargan de procesar entradas de datos y a partir de esto regresar un resultado o realizar una actualización en una base de datos. Usualmente el usuario no interactúa con estos servicios directamente.

Rust cuenta ya con algunos "crates" o librerías que ayudan crear estos servicios de fondo. Información de estos se puede encontrar en la página "Are we web yet?". He decidido seguir la guía  de uno de estos frameworks web para servicios web y clientes web para probar las capacidades de Rust en esta área.

Viendo la sección de frameworks https://www.arewewebyet.org/topics/frameworks/ resaltan algunos por su número de descargas:
  • 1,200,000 descargas para actix-web. Actualmente parece ser el framework más completo, complejo y paralelo. El problema es que puede ser muy complicado para un principiante de Rust. Puede usarse en la actual versión estable de Rust y usa funcionalidades async de Rust. 
  • 734,000 descargas para iron. Es un framework que actualmente no ha sido mantenido considerablemente pero dadas las descargas parece ser que era importante.
  • 515,000 descargas para rocket. En la versión actual de rocket la versión actual estable de Rust no es suficiente para que compile. Rocket requiere algunas funcionalidades "experimentales" que se están estabilizando y/o se planean remover de Rocket para que Rocket pueda compilar en la versión estable de Rust. Pero por ahora para usar rocket necesitamos la versión "nightly" de Rust. Eventualmente esta planeado que pueda usarse con la versión estable de Rust. A pesar de esto hay compañías que ya están usando Rocket en producción según lo mencionado en la plática "Rocket: Securing the Web at Compile-time" que puede encontrarse en Youtube. Rocket también actualmente no parece usar las funcionalidades de async de Rust así que parece ser una buena opción para aplicaciones pequeñas que no se enfrentan a una gran demanda. Para aplicaciones más demandantes actix-web podría ser una mejor opción.
  • 402,000 descargas para wrap. Este framework parece ser un poco más nuevo que Rocket. A diferencia de Rocket este puede usarse en la versión estable actual de Rust y usa funcionalidades async de Rust. 
Para esta prueba quiero un framework simple que me permita hacer un API REST que reciba y regrese JSON. Por lo que me decidí por Rocket ya que parece más amigable para principiantes de Rust. A continuación seguiré el proceso de ejemplo descrito en la página oficial de Rocket: https://rocket.rs/v0.4/guide/getting-started/

El primer paso es crear un proyecto usando "cargo new" y instalando Rust Nightly usando "rustup default nightly":

Creando el proyecto y instalando rust nightly usando "rustup default nightly"
Para agregar Rocket a nuestro proyecto solo es cuestión de agregar rocket al archivo "cargo.toml". 

[package]
name = "rocket-test"
version = "0.1.0"
authors = ["MiUsuario <micorreo@email.com>"]
edition = "2018"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
rocket = "0.4.5"

Como se puede ver la versión de rocket al momento de esta publicación es 0.4.5. El siguiente paso es copiar y pegar el código de ejemplo en el archivo "main.rs", en este caso cambié el mensaje a "Hola, mundo! Estoy usando Rocket":


#![feature(proc_macro_hygiene, decl_macro)]

#[macro_use] extern crate rocket;

#[get("/")]
fn index() -> &'static str {
    "Hola, mundo! Estoy usando Rocket"
}

fn main() {
    rocket::ignite().mount("/", routes![index]).launch();
}

Como podemos ver Rocket hace uso de atributos de Rust. Los atributos son las líneas que empiezan con "#" y afectan lo que sigue inmediatamente después de estos. Cuando un atributo afecta todo un ejecutable o toda una librería o todo un módulo se usa "#!" en lugar de sólo "#".

Como podemos ver por el uso de "#![feature(...)]" Rocket usa ciertas características/features de Rust que se activan con la línea "#![feature(proc_macro_hygiene, decl_macro)]". Luego tenemos que activar el uso de macros de Rocket (macros externos) usando la línea "#[macro_use] extern crate rocket;".

Usando el atributo "#[get("/")]" parece ser la forma en la que etiquetamos una función para que Rocket la maneje. Supongo que estos serán análogos a las funciones GET, PUT, DELETE y POST que se usan en HTTP/REST. En este caso la función "index" regresa un "str" que vive durante toda la vida del ejecutable, es decir que tiene un tiempo de vida (lifetime) del tipo "'static". Como este "str" vive durante toda la ejecución del programa entonces podemos regresar una referencia "&'static" sin problema de que deje de vivir, ya que vivirá durante todo el tiempo de ejecución del programa.

Finalmente tenemos la línea en el main que es "rocket::ignite().mount("/", routes![index]).launch();" donde estamos llamando varios métodos de la librería de Rocket. Por el uso del signo de exclamación "!" puedo ver que "routes!" se trata de una macro. Y le estamos pasando nuestra función "index".

Siguiendo la guía todavía no se nos explica a detalle que es todo esto, pero podemos correrlo ya usando el comando "cargo run".

Como esta es la primera vez que se ejecuta se tienen que descargar y compilar Rocket y sus dependencias de la página web cartes.io:

El resultado de ejecutar "cargo run" por primera vez. Se descargan las dependencias y después de descargar se compilan.
Una vez que esperamos a que se descarguen y se compilen las dependencias podremos ejecutar "cargo run" y ver Rocket corriendo:
El resultado de correr nuestro programa usando "cargo run" después de que ya tenemos las dependencias descargadas y compiladas
Mientras esta esto corriendo en la consola podemos abrir un navegador de internet como Google Chrome y ir a la página "http://localhost:8000" para ver el resultado de este pequeño ejemplo:

Google Chrome con la página http://localhost:8000
Como podemos ver la página nos muestra el texto que escribimos en la función "index". Con esto tenemos una pequeña aplicación web en Rust usando Rocket con lo que fueron pasos muy simples:

  1. "rustup default nightly"
  2. "cargo new rocket-test" y "cd rocket-test" 
  3. Agregar "rocket = "0.4.5"" como dependencia en Cargo.toml
  4. Las 12 líneas de código en main.rs que vienen en el ejemplo inicial de Rocket
  5. "cargo run"
  6. Esperar que se descarguen y compilen las dependencias
No olvidemos que podemos usar "cargo run --release" en lugar de "cargo run" para compilar con optimizaciones y menos información de debug.


Fuentes:

sábado, 22 de febrero de 2020

Creando videojuegos en Rust lang (Parte 34)

En esta entrada instalaré Rust en otra computadora y haré la instalación de VSCode (Visual Studio Code) ya que en las entradas anteriores usaba Notepad++. Ahora quiero probar las funcionalidades de autocompletado que proporciona usar VSCode conectado con un plugin/servidor del idioma Rust.

Para la instalación de Rust los pasos son similares a los vistos en la primera entrada (ver entrada). Vamos a https://www.rust-lang.org/tools/install y en mi caso detectó que estoy usando Windows. Luego seleccionamos la versión de rustup que deseamos instalar ya sea 32-bit o 64-bit. Ejecutamos el instalador y seguimos las instrucciones. Si es necesario en algún punto nos va a pedir instalar algunas herramientas de C++ de las páginas de windows. Estas herramientas son el "Visual C++ Build Tools" que pueden ser seleccionados e instalados desde el instalador de Visual Studio, sin necesidad de instalar Visual Studio completo.

Para instalar Visual Studio Code (VSCode) los pasos son similares a los descritos en las segunda entrada (ver entrada). La página de opciones de descarga es https://code.visualstudio.com/Download y seleccionamos la opción que deseamos según el método de instalación y nuestro sistema operativo.  Ejecutamos el instalador si usamos el instalador ejecutable (.exe) y seguimos las pantallas.

Una vez instalados todas estas herramientas podemos abrir la línea de comandos ("cmd"), navegar a una carpeta de proyectos usando "cd <directorio del proyecto>" y usar el comando cargo new para que cargo cree una carpeta con todo lo que necesitamos para un nuevo proyecto de Rust. En este caso siempre empiezo con "cargo new helloworld".

Uso el comando "cd helloworld". Después uso el comando "code ." para abrir Visual Studio Code en esta misma carpeta. Genera el siguiente mensaje:

Mensaje de la esquina inferior derecha donde pregunta por Git
Visual Studio Code detecta la carpeta oculta ".git" que generó "cargo" en esta carpeta. Me dice que no encontró Git. Git es un programa gratuito de control de versiones de archivos de texto. Git es un estándar implícito para administrar las versiones de código de bases de código fuente. De hecho GitHub tiene como su base el modelo de Git. En el caso del mundo de los juegos de vídeo tenemos que administrar recursos que no sólo son el código. Recursos como videos, imágenes, fuentes de texto, texturas, etc. Estos recursos no encajan muy bien en el modelo de Git por lo cual para administrarlos podemos usar otras opciones con un modelo diferente y más intuitivo, como por ejemplo el usado por SVN. Sin embargo para juegos muy muy pequeños donde sólo tenemos 1 desarrollador y donde no se espera que cambien muchos los recursos no debería de ser un problema muy grande usar Git. Para instalar Git tenemos que ir a la página de Git (https://git-scm.com/downloads) y descargar el instalador de Git.

Para instalar Git navegamos las pantallas del instalador dejando las opciones predeterminadas. Para ver si esto resuelve nuestro problema cierro la ventana del Visual Studio Code y vuelvo a abrirla con el comando "code .". Y ahora VSCode sí lo detecta ya que no aparece ese mensaje anterior de "Git not found".

Ahora abrimos el archivo principal "main.rs" y podemos ver como muestra los colores en la sintaxis del idioma y en la esquina inferior derecha dice "Rust" que muestra que detectó el idioma.
Aparece "Rust" en la esquina inferior derecha y la sintaxis de Rust se muestra en los colores utilizados en el archivo "main.rs"
Visual Studio Code tiene la posibilitdad de comunicarse con un servidor de idioma o "language server" que le permite agregar funcionalidades del idioma como autocompletado. En el caso de Rust tenemos dos opciones principales al momento de escribir esta entrada: rls (rust language server) y rust-analyzer. Resulta que al momento de escribir esta entrada parece ser que rust-analyzer tiene más funcionalidad y forma parte de lo que sería el esfuerzo para llegar a "rls 2.0". De hecho intenté instalar rls en mi instalación inicial pero por alguna razón no logró funcionar, es por esta razón que ahora intentaré con rust-analyzer.

En la fecha que se escribió esta entrada "rust-analyzer" ya fue liberado al púbico y puesto como una extensión pública para Visual Studio Code. Para instalar rust-analyzer simplemente lo buscamos en el menú para instalar extensiones de Visual Studio Code y le damos click en install:

La sección de extensiones. El buscador de extensiones para buscar "rust-analyzer". La opción de "Rust-analyzer" y el botón de "Install".
Con estos pasos instala la extensión. Después de instalar la extensión puede que una notificación pida descargar el servidor del idioma para que la extensión pueda funcionar correctamente. Si este es el caso le damos click en la opción de descargar.

El popup que aparece en la esquina inferior derecha pidiendo que se instale el servidor del idioma ("Language server"). Le damos click en la opción que dice "Download now".
Puede que después de instalarse puede que siga mandando mensajes de error. Por ejemplo a mi me apareció el mensaje "rust-analyzer failed to load workspace: Failed to find sysroot for cargo.toml file ...".

De hecho alrededor del día en el que escribí esta entrada publicaron en el Reddit oficial de Rust una pregunta sobre exactamente este error: https://www.reddit.com/r/rust/comments/f6rxyj/rustanalyzer_failed_to_load_workspace_failed_to/. Mencionan que la forma de resolver este error es ejecutar el comando "rustup component add rust-src" en la consola:

Ejecutando "rustup component add rust-src"
Con esto el error que aparecía en Visual Studio Code desaparece. El comando anterior "rustup component add rust-src" va a ser agregado como parte de la instalación en futuras versiones. Este paso venía descrito en la página oficial de rust-analyzer en la sección de instalación: https://rust-analyzer.github.io/manual.html

También no nos olvidemos de instalar "rustfmt" y "clippy" con los comandos correspondientes en la consola:
  • rustup component add rustfmt
  • rustup component add clippy
Confirmando que rustfmt y clippy están instalados
Para probar que rust-analyzer funciona correctamente podemos probar presionando las teclas que vienen en la guía: https://github.com/rust-analyzer/rust-analyzer/blob/master/docs/user/features.md o usando las opciones de autocompletado que también vienen descritas en el mismo documento:
  • "Ctrl+t" para buscar los tipos de datos, funciones y módulos. Usamos "*" para buscar en las dependencias en vez de nuestro espacio de trabajo actual. Usamos "#" para buscar funciones en vez de tipos de datos.
  • "Ctrl+Shift+o" para buscar símbolos/texto en el documento que está abierto.
  • Proporciona sugerencias según lo que se escribe y también hay otras expresiones que pueden usarse para generar código más rápidamente.
Autocompletado de Rust en Visual Studio Code

jueves, 26 de diciembre de 2019

Creando videojuegos en Rust lang (Parte 33)

En esta entrada usaré las opciones de ggez en cuestión a administración de archivos. En las entradas anteriores se mostraba como los archivos de recursos que usamos en el juego (los sonidos e imágenes) se tenían que colocar manualmente dentro de la carpeta "resources" que a su vez se tenía que colocar dentro de la carpeta "debug" que se genera cuando compilamos el programa con "cargo run". Esto sólo se cumple si compilamos en modo "debug" usando "cargo run", la carpeta sería "release" si compilamos en modo "release" usando "cargo run --release".

Estos son los archivos que teníamos en el ejemplo de la entrada anterior cuando compilamos en modo debug usando "cargo run":
La carpeta "target\debug\resources"
Si no movemos los archivos a este lugar manualmente entonces nuestro programa no podrá encontrar los recursos y mostará el siguiente mensaje de error:
Mensaje de error "El sistema no puede encontrar el archivo especificado" y menciona la carpeta "target/release/resouces"
Para evitar requerir que el usuario mueva estos archivos al lugar adecuado y manejar archivos de una mejor forma "ggez" nos proporciona con funcionalidades de manejo de archivos. Estas funciones las podemos ver ejemplificadas en los ejemplos en GitHub: https://github.com/ggez/ggez/blob/master/examples/files.rs. Las líneas que debemos de agregar son las líneas que agregan la carpeta de nuestro programa y una carpeta "resources". Este es el bloque de código que contiene las líneas que debemos agregar:

    let mut cb = ContextBuilder::new("ggez_files_example", "ggez");

    // We add the CARGO_MANIFEST_DIR/resources to the filesystems paths so
    // we we look in the cargo project for files.
    // Using a ContextBuilder is nice for this because it means that
    // it will look for a conf.toml or icon file or such in
    // this directory when the Context is created.
    if let Ok(manifest_dir) = env::var("CARGO_MANIFEST_DIR") {
        let mut path = path::PathBuf::from(manifest_dir);
        path.push("resources");
        println!("Adding path {:?}", path);
        cb = cb.add_resource_path(path);
    }

    let (ctx, _) = &mut cb.build()?;

Ponemos nuestros archivos en la carpeta "resources" en la carpeta de nuestro proyecto:
Ahora podemos dejar la carpeta "resources" en donde está nuestro archivo "Cargo.toml"
Entonces los cambios que debemos de hacer en nuestro código son los siguientes:

  • Hay que agregar las referencias a "use std::env;", "use std::path;"
    • use std::env;
      use std::path;
      
  • El resultado es entonces que nuestro código en nuestra función "main" ahora es el siguiente:
    • let mut constructor_de_contexto = ContextBuilder::new(
              "hello_ggez", "autor")
          .conf(configuracion);
      if let Ok(directorio_de_cargo) = env::var(
              "CARGO_MANIFEST_DIR") {
          let mut carpeta_cargo = path::PathBuf::from(
              directorio_de_cargo);
          carpeta_cargo.push("resources");
          println!("Agregando carpeta de recursos {:?}", 
              carpeta_cargo);
          constructor_de_contexto = constructor_de_contexto
              .add_resource_path(carpeta_cargo);
      }
      let (ref mut contexto, 
           ref mut bucle_de_juego) = constructor_de_contexto
           .build().unwrap();
      
Y como resultado de los cambios anteriores este es nuestro código con los cambios más recientes:


  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
use ggez::{conf, ContextBuilder, Context, 
           event, graphics, GameResult};
use std::time::{Duration, Instant};

use std::env;
use std::path;

use rand::prelude::*;

use std::collections::VecDeque;

use ggez::audio;
use ggez::audio::SoundSource;

use ggez::graphics::Text;

const DIMENSION_DEL_TABLERO: (i16, i16) = (10, 10);
const DIMENSION_DE_CELDAS_DEL_TABLERO: (i16, i16) = (32, 32);
const DIMENSION_DE_VENTANA: (f32, f32) = (
    DIMENSION_DEL_TABLERO.0 as f32 * 
    DIMENSION_DE_CELDAS_DEL_TABLERO.0 as f32,
    DIMENSION_DEL_TABLERO.1 as f32 * 
    DIMENSION_DE_CELDAS_DEL_TABLERO.1 as f32,
);

const ACTUALIZACIONES_POR_SEGUNDO: f32 = 8.0;
const MILISEG_POR_ACTUALIZACION: u64 = (
    (1.0 / ACTUALIZACIONES_POR_SEGUNDO) * 1000.0) as u64;

#[derive(PartialEq)]
enum Direccion {
    Arriba,
    Abajo,
    Izquierda,
    Derecha,
}

#[derive(PartialEq)]
enum Estado {
    Jugando,
    GameOver
}

#[derive(PartialEq)]
enum Fondo {
    Estado1,
    Estado2
}

struct EstadoDelJuego {
    jugador_x: i16,
    jugador_y: i16,
    cuerpo: VecDeque<(i16, i16)>,
    direccion: Direccion,
    estado: Estado,
    comida_x: i16,
    comida_y: i16,
    sonido: audio::Source,
    puntaje: i16,
    imagen_comida: graphics::Image,
    imagen_serpiente: graphics::Image,
    ultima_actualizacion: std::time::Instant,
    spritebatch: graphics::spritebatch::SpriteBatch,
    fondo: Fondo
}

impl ggez::event::EventHandler for EstadoDelJuego {
    fn update(&mut self, 
            _contexto: &mut Context) -> GameResult<()> {
        if (Instant::now() - 
            self.ultima_actualizacion >= 
            Duration::from_millis(MILISEG_POR_ACTUALIZACION)) &
           (self.estado == Estado::Jugando) {
            if self.fondo == Fondo::Estado1 {
                self.fondo = Fondo::Estado2;
            } else {
                self.fondo = Fondo::Estado1;
            }
            match self.direccion {
                Direccion::Arriba => {
                self.jugador_y = self.jugador_y - 1;
                if self.jugador_y < 0 {
                    self.estado = Estado::GameOver;
                    self.jugador_y = self.jugador_y + 1;
                } else {
                    for parte in self.cuerpo.iter() {
                        if parte.0 == self.jugador_x &&
                           parte.1 == self.jugador_y {
                            self.estado = Estado::GameOver;
                            self.jugador_y = self.jugador_y + 1;
                            break;
                        }
                    }
                }},
                Direccion::Abajo => {
                self.jugador_y = self.jugador_y + 1;
                if self.jugador_y > DIMENSION_DEL_TABLERO.1 - 1 {
                    self.estado = Estado::GameOver;
                    self.jugador_y = self.jugador_y - 1;
                } else {
                    for parte in self.cuerpo.iter() {
                        if parte.0 == self.jugador_x &&
                           parte.1 == self.jugador_y {
                            self.estado = Estado::GameOver;
                            self.jugador_y = self.jugador_y - 1;
                            break;
                        }
                    }
                }},
                Direccion::Izquierda => {
                self.jugador_x = self.jugador_x - 1;
                if self.jugador_x < 0 {
                    self.estado = Estado::GameOver;
                    self.jugador_x = self.jugador_x + 1;
                } else {
                    for parte in self.cuerpo.iter() {
                        if parte.0 == self.jugador_x &&
                           parte.1 == self.jugador_y {
                            self.estado = Estado::GameOver;
                            self.jugador_x = self.jugador_x + 1;
                            break;
                        }
                    }
                }},
                Direccion::Derecha => {
                self.jugador_x = self.jugador_x + 1;
                if self.jugador_x > DIMENSION_DEL_TABLERO.0 - 1 {
                    self.estado = Estado::GameOver;
                    self.jugador_x = self.jugador_x - 1;
                } else {
                    for parte in self.cuerpo.iter() {
                        if parte.0 == self.jugador_x &&
                           parte.1 == self.jugador_y {
                            self.estado = Estado::GameOver;
                            self.jugador_x = self.jugador_x - 1;
                            break;
                        }
                    }
                }}
            }
            if self.jugador_x == self.comida_x &&
               self.jugador_y == self.comida_y {
                let _correr_sonido = self.sonido.play_detached();
                let mut rng = rand::thread_rng();
                let mut ini_comida_x = rng.gen_range::<i16, i16, i16>(0, 
                    DIMENSION_DEL_TABLERO.0);
                let mut ini_comida_y = rng.gen_range::<i16, i16, i16>(0, 
                    DIMENSION_DEL_TABLERO.1);
                loop {
                    if self.jugador_x != ini_comida_x &&
                       self.jugador_y != ini_comida_y {
                        let mut ocupado = false;
                        for parte_de_cuerpo in self.cuerpo.iter() {
                            if parte_de_cuerpo.0 == ini_comida_x &&
                               parte_de_cuerpo.1 == ini_comida_y {
                                ocupado = true;
                                break;
                            }
                        }
                        if ocupado == false {
                            break;
                        }
                    }
                    ini_comida_x = rng.gen_range::<i16, i16, i16>(0, 
                        DIMENSION_DEL_TABLERO.0);
                    ini_comida_y = rng.gen_range::<i16, i16, i16>(0, 
                        DIMENSION_DEL_TABLERO.1);
                }
                self.comida_x = ini_comida_x;
                self.comida_y = ini_comida_y;
                self.cuerpo.push_front((self.jugador_x, self.jugador_y));
                self.puntaje = self.puntaje + 1;
            } else {
                self.cuerpo.pop_back();
                self.cuerpo.push_front((self.jugador_x, self.jugador_y));
            }
            self.ultima_actualizacion = Instant::now();
        }
        Ok(())
    }
    
    fn draw(&mut self, 
            contexto: &mut Context) -> GameResult<()> {
        graphics::clear(contexto, [0.0, 1.0, 0.0, 1.0].into());
        
        // Dibujar el fondo
        for x in 0..DIMENSION_DEL_TABLERO.0 {
            for y in 0..DIMENSION_DEL_TABLERO.1 {
                let x = x as f32;
                let y = y as f32;
                if self.fondo == Fondo::Estado1 {
                    let p = graphics::DrawParam::new()
                    .src(ggez::graphics::Rect::new(
                     0.0 as f32, 
                     0.0 as f32,
                     0.5 as f32,
                     1 as f32))
                    .dest(ggez::mint::Point2{x : x * 
                     DIMENSION_DE_CELDAS_DEL_TABLERO.0 as f32, 
                                             y : y * 
                     DIMENSION_DE_CELDAS_DEL_TABLERO.1 as f32});
                    self.spritebatch.add(p);
                } else {
                    let p = graphics::DrawParam::new()
                    .src(ggez::graphics::Rect::new(
                    0.5 as f32, 
                    0.0 as f32,
                    0.5 as f32,
                    1 as f32))
                    .dest(ggez::mint::Point2{x : x * 
                     DIMENSION_DE_CELDAS_DEL_TABLERO.0 as f32, 
                                             y : y * 
                     DIMENSION_DE_CELDAS_DEL_TABLERO.1 as f32});
                    self.spritebatch.add(p);
                }
            }

        }
        let param = graphics::DrawParam::new()
            .dest(ggez::mint::Point2 {
                x: 0.0,
                y: 0.0
            })
            .offset(ggez::mint::Point2{ x: 0.0, y: 0.0});
        graphics::draw(contexto, &self.spritebatch, param)?;
        self.spritebatch.clear();
        
        // Dibujar el cuerpo de la serpiente
        for parte_del_cuerpo in self.cuerpo.iter() {
            let rectangulo_cuerpo = ggez::graphics::Rect::new(
                (parte_del_cuerpo.0 *
                 DIMENSION_DE_CELDAS_DEL_TABLERO.0) as f32, 
                (parte_del_cuerpo.1 *
                 DIMENSION_DE_CELDAS_DEL_TABLERO.1) as f32,
                DIMENSION_DE_CELDAS_DEL_TABLERO.0 as f32,
                DIMENSION_DE_CELDAS_DEL_TABLERO.1 as f32);
            let grafico_de_cuerpo = graphics::Mesh::new_rectangle(
                contexto,
                graphics::DrawMode::fill(),
                rectangulo_cuerpo,
                [9.0, 0.1, 0.0, 1.0].into())?;
            graphics::draw(contexto, 
                &grafico_de_cuerpo, 
                (ggez::mint::Point2 { x: 0.0, y: 0.0 },))?;
        }
        // Dibujar el jugador
        let rectangulo_jugador = ggez::graphics::Rect::new(
            (self.jugador_x *
             DIMENSION_DE_CELDAS_DEL_TABLERO.0) as f32, 
            (self.jugador_y *
             DIMENSION_DE_CELDAS_DEL_TABLERO.1) as f32,
            DIMENSION_DE_CELDAS_DEL_TABLERO.0 as f32,
            DIMENSION_DE_CELDAS_DEL_TABLERO.1 as f32);
        let grafico_de_jugador = graphics::Mesh::new_rectangle(
            contexto,
            graphics::DrawMode::fill(),
            rectangulo_jugador,
            [0.3, 0.3, 0.0, 1.0].into())?;
        graphics::draw(contexto, 
            &grafico_de_jugador, 
            (ggez::mint::Point2 { x: 0.0, y: 0.0 },))?;
        // Dibujar la comida
        let rectangulo_comida = ggez::graphics::Rect::new(
            (self.comida_x *
             DIMENSION_DE_CELDAS_DEL_TABLERO.0) as f32, 
            (self.comida_y *
             DIMENSION_DE_CELDAS_DEL_TABLERO.1) as f32,
            DIMENSION_DE_CELDAS_DEL_TABLERO.0 as f32,
            DIMENSION_DE_CELDAS_DEL_TABLERO.1 as f32);
        let grafico_de_comida = graphics::Mesh::new_rectangle(
            contexto,
            graphics::DrawMode::fill(),
            rectangulo_comida,
            [0.0, 0.0, 1.0, 1.0].into())?;
        graphics::draw(contexto, 
            &grafico_de_comida, 
            (ggez::mint::Point2 { x: 0.0, y: 0.0 },))?;
        graphics::draw(contexto, 
            &self.imagen_comida, 
            (ggez::mint::Point2 { 
             x: (self.comida_x *
             DIMENSION_DE_CELDAS_DEL_TABLERO.0) as f32, 
             y: (self.comida_y *
             DIMENSION_DE_CELDAS_DEL_TABLERO.1) as f32 },))?;
        if self.estado == Estado::GameOver {
            graphics::draw(
                contexto,
                &self.imagen_serpiente,
                (ggez::mint::Point2 { x: 112.0, y: 112.0 },)
            )?;
            let texto_puntaje = Text::new(format!(
                "Game Over\nPuntaje:{}", self.puntaje));
            graphics::draw(
                contexto,
                &texto_puntaje,
                (ggez::mint::Point2 { x: 112.0, y: 112.0 },)
            )?;
        }
        // Mandando al generador de gráficos
        graphics::present(contexto)?;
        ggez::timer::yield_now();
        Ok(())
    }
    
    fn key_down_event(
        &mut self,
        _ctx: &mut Context,
        keycode: ggez::event::KeyCode,
        _keymod: ggez::event::KeyMods,
        _repeat: bool,
    ) {
        match keycode {
            ggez::event::KeyCode::Up => {
                if self.direccion != Direccion::Abajo {
                    self.direccion = Direccion::Arriba
                }
            },
            ggez::event::KeyCode::Down => {
                if self.direccion != Direccion::Arriba {
                    self.direccion = Direccion::Abajo
                }
            },
            ggez::event::KeyCode::Left => {
                if self.direccion != Direccion::Derecha {
                    self.direccion = Direccion::Izquierda
                }
            },
            ggez::event::KeyCode::Right => {
                if self.direccion != Direccion::Izquierda {
                    self.direccion = Direccion::Derecha
                }
            },
            _ => (),
        }
        ()
    }
}

fn main() {
    let mut rng = rand::thread_rng();
    let mut ini_comida_x = rng.gen_range::<i16, i16, i16>(0, 
        DIMENSION_DEL_TABLERO.0);
    let mut ini_comida_y = rng.gen_range::<i16, i16, i16>(0, 
        DIMENSION_DEL_TABLERO.1);
    loop {
        if DIMENSION_DEL_TABLERO.0 / 4 != ini_comida_x &&
           DIMENSION_DEL_TABLERO.1 / 2 != ini_comida_y {
            break;
        }
        ini_comida_x = rng.gen_range::<i16, i16, i16>(0, 
            DIMENSION_DEL_TABLERO.0);
        ini_comida_y = rng.gen_range::<i16, i16, i16>(0, 
            DIMENSION_DEL_TABLERO.1);
    }
    let mut configuracion = conf::Conf::new();
    configuracion.window_setup = conf::WindowSetup::default()
        .title("Snake");
    configuracion.window_mode = conf::WindowMode::default()
        .dimensions(DIMENSION_DE_VENTANA.0, 
                    DIMENSION_DE_VENTANA.1);
    let mut constructor_de_contexto = ContextBuilder::new(
            "hello_ggez", "autor")
        .conf(configuracion);
    if let Ok(directorio_de_cargo) = env::var(
            "CARGO_MANIFEST_DIR") {
        let mut carpeta_cargo = path::PathBuf::from(
            directorio_de_cargo);
        carpeta_cargo.push("resources");
        println!("Agregando carpeta de recursos {:?}", 
            carpeta_cargo);
        constructor_de_contexto = constructor_de_contexto
            .add_resource_path(carpeta_cargo);
    }
    let (ref mut contexto, 
         ref mut bucle_de_juego) = constructor_de_contexto
         .build().unwrap();
    let fondo = graphics::Image::new(contexto,
                    "/fondo.png").unwrap();
    let estado_del_juego = &mut EstadoDelJuego {
        jugador_x: DIMENSION_DEL_TABLERO.0 / 4,
        jugador_y: DIMENSION_DEL_TABLERO.1 / 2,
        cuerpo: VecDeque::new(),
        direccion: Direccion::Derecha,
        estado: Estado::Jugando,
        comida_x: ini_comida_x,
        comida_y: ini_comida_y,
        sonido: audio::Source::new(
            contexto, "/sonido.ogg").unwrap(),
        puntaje: 0i16,
        imagen_comida: graphics::Image::new(
            contexto, "/comida.png").unwrap(),
        imagen_serpiente: graphics::Image::new(
            contexto, "/serpiente.png").unwrap(),
        ultima_actualizacion: Instant::now(),
        spritebatch: graphics::spritebatch::SpriteBatch::new(
                     fondo),
        fondo: Fondo::Estado1
    };
    event::run(contexto, bucle_de_juego, 
        estado_del_juego).unwrap();
}