1votos

Snake en F#

por josejuan hace 5 años

Snake corriendo bajo Linux (versión Open Source de F# 3.0). Permite jugar a tantos Snakes simultáneos como se desee.

Fernando de Genbetadev dice que si haces un Snake en un lenguaje, entonces, ese lenguaje ya casi no tiene secretos para tí.

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
open System 
open System.Drawing 
open System.Windows.Forms 
 
let rec dropLast l = 
        match l with 
        | [] -> [] 
        | head :: tail :: [] -> head :: [] 
        | head :: tail -> head :: dropLast tail 
 
let ARENA_SIZE = 30 
let BLOCK_SIZE = 10 
let ARENA_WIDTH = ARENA_SIZE * BLOCK_SIZE 
let ARENA_HEIGHT = ARENA_SIZE * BLOCK_SIZE 
 
type Snake( playerName : string     // window Player 
          , arenaSize : int         // world size in blocks 
          , blockSize : int         // block pixels size 
          , speed : int             // milliseconds to think next step 
          , stepsToFeed : int       // each 'n' steps one more food 
          ) = 
  let arenaL = arenaSize * blockSize 
  let form = new Form( Text = sprintf "F# %s Snake!" playerName 
                     , Visible = true 
                     , TopMost = true 
                     , Width = arenaL 
                     , Height = arenaL 
  let mutable snake = [(arenaSize / 2, arenaSize / 2)] 
  let mutable toFeed = stepsToFeed 
  let mutable direction = 0 
  let directions = [(0, -1); (-1, 0); (0, 1); (1, 0)] 
  let mutable food : list<int * int> = [] 
  let t = new Timer() 
  let cells = [for x in [0..arenaSize - 1] do for y in [0..arenaSize - 1] do yield (x, y)] 
  let rnd = new Random() 
 
  do form.MouseDown.Add ( 
          fun e -> 
             let d = if e.Button = MouseButtons.Right then -1 else 1 
             direction <- (4 + direction + d) % 4 
   
  do form.Paint.Add ( 
          fun e -> 
 
            toFeed <- toFeed - 1 
            if toFeed <= 0 
              then 
                let usedCells = List.append food snake 
                let freeCells = List.filter (fun c -> not (List.exists ((=)c) usedCells)) cells 
                toFeed <- stepsToFeed 
                food <- freeCells.[rnd.Next(List.length freeCells)] :: food 
 
            let (hx', hy') as h' = 
                let (hx, hy) :: _ = snake 
                let (dx, dy) = directions.[direction] 
                ((arenaSize + hx + dx) % arenaSize, (arenaSize + hy + dy) % arenaSize) 
 
            if List.exists ((=)h') snake 
              then 
                do printfn "%s ha perdido!" playerName 
                do t.Stop() 
              else 
                if List.exists ((=)h') food 
                  then 
                    food <- List.filter ((<>)h') food 
                  else 
                    snake <- dropLast snake 
                snake <- h' :: snake 
 
            let g = e.Graphics 
            let draw xs color = 
                for (x, y) in xs do 
                  g.DrawRectangle(color, blockSize * x, blockSize * y, blockSize, blockSize) 
            g.Clear(Color.White) 
            draw snake Pens.Green 
            draw food Pens.Red 
   
  do t.Tick.Add(fun evArgs -> form.Refresh()) 
  do t.Interval <- speed 
  do t.Start() 
 
  do Application.Run(form) 
 
 
// == running some Snake Windows at same time ==================================== 
 
Async.Start( async { new Snake("Peter", 20, 10, 500, 10) } ) 
Async.Start( async { new Snake("Billy", 50, 5, 150, 3) } ) 
new Snake("John", 30, 20, 250, 15) 
5 comentarios
0votos

Escrito por jmgomez hace 5 años

Muy bonito! Le estás cogiendo el gusto a FSharp o te estás forzando a cogerselo? Opiniones generales? ¡Deberías escribir sobre ello!
1votos

Escrito por josejuan hace 5 años

De momento no me estoy preocupando mucho de que el código sea demasiado correcto o limpio, estoy probando cosas.

Por ejemplo había establecido el giro izq/der con una enumeración North|South|... pero queda mucho mejor con MOD4.

Aún no he terminado de leer la ref del leng de F#, aunque sí la mayoría, por lo que aún no he repasado un aspecto muy importante del lenguaje, y es cómo la gente lo usa para resolver los problemas comunes (ej. duck es una herramienta universal y tremendamente plástica, pero no ayuda al programador en generar código limpio y seguro; pero hay usos originales, elegantes y potentes del mismo).

Lo que sí tengo bastante claro es que F# es un popurrí muy muy (a veces pienso que son la misma cosa) similar al que me encontré con Scala, pero con muchas más "peculiaridades" (ej. magnitudes, diferentes interpretaciones de contexto [currificables/no currificables], etc...) que (para mi gusto) le quitan "seriedad" (que al final se traslada en una base frágil).

En twitter he ido poniendo algunas cuestiones más o menos importantes que (siempre a mi juicio personal y de un NO experto) dan muestra del enfoque que posee (contentar a la principal masa [por volumen] de programadores). Es decir, no es que esté mal hecho ni nada parecido, es que se ha diseñado así a propósito, pero ese propósito ("marketiniano" podríamos decir) no lo comparto (yo, en particular y como individuo).

Una de las pruebas (a mi juicio otra vez) más claras es que hay un soporte muy pobre para trabajar con funciones en lugar de con valores, por ejemplo, no hay un soporte completo para escribir código genérico, las propias implementaciones de "List.map", "Seq.map", "Array.map" lo ponen de manifiesto (pues bastaría con una única función "map").

Y no se trata de poder "simular" el comportamiento, por ejemplo, se podría internar hacer algo como
let map f s =
    match s with
    | :? list<_> -> List.map f s
    | :? seq<_> -> Seq.map f s
    | :? array<_> -> Array.map f s

que, aunque no funciona porque la capacidad de inferencia de tipos de F# está dividida en ciertos tipos (en este caso "inferibles" y "no inferibles") pero quizás pueda apañarse con restricciones de tipo, muestra que el esquema no es flexible.

Tampoco tiene soporte (por defecto) para funciones típicas en programación funcional como `flip`, que puede hacerse fácilmente como:
let inline flip f a b = f b a


Esto no quita para que pueda ser un gran lenguaje, muy popular y cómodo de usar, pero para mí tendrá la misma categoría (personal y subjetiva mía, claro) que (por ejemplo) javascript, un lenguaje que me gusta bastante, pero que tiene ningún tipo de formalización (en el lenguaje).

Otras cuestiones como que estoy acostumbrado a usar notación dot y en F# las pipes (en que el flujo se invierte de izq/der a der/izq) son puramente de costumbre que cambiaré con el tiempo.

En todo caso, ya metidos en harina insistiré en él tanto como pueda, hasta descartarlo definitvamente o incluirlo en mi toolbox :)
0votos

Escrito por jmgomez hace 5 años

Interesante. No sé si lo que comentas, desde el punto de vista del diseño, es herencia de OCaml (nunca lo he mirado) o si por el contrario lo han decidido a la hora de reimplementarlo, supongo que los tiros iran por lo primero.

Con tanto comentario sobre lo impuro que es FSharp (bajo tu punto de vista) me está aumentando el hype de mirarme algún lenguaje más puro como Haskell. Aunque como te he dicho más de una vez, en mi caso me gusta añadir las cosas que miro nuevas a mi stack de desarrollo, quizá aunque no incorpore haskell pueda mejorar un poco mi alma; debo pensarlo. Por lo pronto, FSharp me gusta, aunque sea por darle un nuevo sabor a .NET, prefiero hacer más con menos. Y aunque según tu criterio (que sabes que tengo en cuenta) no sea estrictamente funcional, al menos a abierto mi mente hacia el paradigma de forma que nunca lo hubiera conseguido con CSharp (por mucho linq y genéricos que haya). De nuevo, desde el punto de vista de la productividad se puede contemplar aislar FSharp para el dominio, en cualquier caso tengo que seguir formándome en ese sentido.

Gracias por la respuesta :)
0votos

Escrito por josejuan hace 5 años

Efectivamente, esa es precisamente la idea, ver que F# no nos va a forzar a seguir un paradigma funcional, pero que igualmente puede sernos, en nuestro contexto particular, de utilidad (como cualquier otro lenguaje).

:)

Comenta la solución

Tienes que identificarte para poder publicar tu comentario.