¡Haz un Tamagotchi / Giga Pet simplificado!

9

Tamagotchi y Giga Pets eran pequeños dispositivos electrónicos que simulaban una pequeña mascota virtual. Esta mascota tenía varias estadísticas, como salud, hambre, etc.
Recientemente escribí este ejemplo:

import msvcrt,os,sys;d=m=100;h=s=t=p=0;f=10
while 1:
 os.system('cls'if os.name=='nt'else'clear');print("health:",d,"\nhunger:",h,"\nsleep:",s,"\nfood:",f,"\npotions:",p,"\nmoney:",m);t+=1
 if msvcrt.kbhit():
  k=ord(msvcrt.getch())
  if k==102 and h>8 and f:f-=1;h-=9
  if k==115:s=0
  if k==112 and p:p-=1;d+=9
  if k==98 and m>8:m-=9;p+=1
  if k==116 and m>8:m-=9;f+=1
 if t>99:
  t=0;h+=1;s+=1
  if s>80:s=0;h+=9
  if h>80:d-=1
  if d<1:sys.exit(0)
  if d>79:m+=1

¡Esta es una mascota virtual básica en 467 bytes! Entonces me pregunté qué tan bien podrían hacer los profesionales de golf de código , así que ahora, el desafío.

El reto

Cree un programa que rastree 6 estadísticas de una mascota virtual y las actualice con el tiempo y en respuesta a las aportaciones del usuario. Las estadísticas son: salud y dinero (a partir de 100), comida (a partir de 10) y hambre, sueño y pociones (a partir de 0).

El programa debe actualizar los valores en respuesta a los siguientes eventos:

  • Mientras el programa no recibe ninguna entrada, debe realizar actualizaciones a intervalos regulares (el intervalo entre actualizaciones no debe ser más corto que medio segundo ni más largo que un segundo). Cada actualización hace lo siguiente:

    • El hambre y el sueño aumentan en 1.
    • Si el hambre es 80 o superior, la salud disminuye en 1.
    • Si Sleep es 80 o superior, se restablece a 0, y Hunger aumenta en 9 adicionales.
    • Si la salud es 80 o superior, el dinero aumenta en 1.
    • Si la salud es 0, el programa se cierra.
  • El programa también debe responder inmediatamente al presionar las siguientes teclas por el usuario (esto significa que necesitará usar una función o biblioteca de idioma que pueda detectar una tecla presionada y responder de inmediato, en lugar de solo leer desde la entrada estándar), realizando las siguientes acciones:

    • f: Si el hambre es mayor que 8 y la comida no es cero, la comida disminuye en 1 y el hambre disminuye en 9.
    • s: El sueño se restablece a 0.
    • p: Si Pociones es mayor que cero, las Pociones disminuyen en 1 y la Salud aumenta en 9.
    • b: Si el dinero es mayor que 8, entonces el dinero se reduce en 9 y las pociones se incrementan en 1.
    • t: Si el dinero es mayor que 8, entonces el dinero se reduce en 9 y los alimentos aumentan en 1.

Siempre que los valores de las estadísticas cambien, deben mostrarse en la pantalla en el formulario . Las seis estadísticas deben mostrarse cada vez que alguna de ellas cambie; y las estadísticas dentro de una pantalla deben estar separadas por comas o por nuevas líneas.Stat: value

Este desafío sigue las reglas normales de : gana el programa más corto que cumpla con la especificación anterior. (Tenga en cuenta que, como de costumbre, si el idioma es más nuevo que la competencia, la presentación debe estar marcada como no competitiva).

ender_scythe
fuente
No dice cuáles son las estadísticas iniciales
azul el
1
Las salidas deben ser "fáciles de leer" es ambiguo y, por lo tanto, "una vez por segundo". Debe enviar los desafíos al sandbox para obtener comentarios y mejorarlos antes de publicar en main ...
FlipTack el
1
¿Cómo es esto poco claro? Desearía que no estuviera claro tenía más detalles, porque en sí mismo no está claro.
ender_scythe
2
Sandbox ya no ayuda, ya que hay respuestas. Las personas que han marcado esto como poco claro deberían decir por qué no es claro, en lugar de no serlo.
ender_scythe
1
@BlueEyedBeast (y otros votantes cercanos) También tengo curiosidad sobre lo que no está claro. La versión original tenía algunos problemas importantes, pero parece que las ediciones solucionaron eso. Por lo menos, fue lo suficientemente claro para Carcigenicate y yo para proporcionar presentaciones. Comparta algunos detalles sobre lo que no está claro para que ender_scythe pueda mejorarlo.
Ray

Respuestas:

6

C, 424 406 386 357 bytes

#define x(a,b,c)if(*#a==C){if(b>8){c-=9;}Z=0;}
L,M=100,H,S=-1,F=10,P,Z,C;t(){H++;S++;L-=H>79;if(S>79)S=0,H+=9;Z=1/L/2;alarm(1);}main(){nodelay(initscr(),L=M);signal(14,t);for(t(H=S);C=getch();Z=Z||printf("\rhealth: %d,hunger: %d,sleep: %d,food: %d,potions: %d,money: %d\n",L,H,S,F,P,M)){x(s,9,S=9;S)x(p,P+8,P--;L+=18;L)x(f,H,F--;H)x(b,M,P++;M)x(t,M,F++;M)}}

Aprecio la necesidad de entrada sin formato y actualizaciones asincrónicas en la especificación del problema. Aunque eso requirió un poco de sobrecarga de la configuración de ncurses y los controladores de señal, es bueno tener el desafío ocasional de que (con suerte) uno de los idiomas dedicados al golf no lo gane automáticamente.

No especificaste exactamente cómo se indica el final del juego, por lo que este perece con el tradicional grito de muerte de Tamagotchi de "excepción de punto flotante (núcleo volcado)".

Sin golf

/* Playing a bit fast and loose with the prototyping rules by omitting these;
 * none of the functions I'm using from them *exactly* match the default
 * prototype of `int f();`
 */
//#include <curses.h>
//#include <stdio.h>
//#include <signal.h>
//#include <unistd.h>

#define x(a,b,c)if(*#a==C){if(b>8){c-=9;}Z=0;}
L,M=100,H,S=-1,F=10,P,Z,C;

t() {
    H++;
    S++;
    L-=H>79;
    if(S>79)S=0,H+=9;
    Z=1/L/2;//0 if L>0. otherwise the pet dies of a floating point error
    alarm(1);
}

main(){
    nodelay(initscr(),L=M);
    signal(14,t);
    for(t(H=S); C=getch(); Z=Z||printf("\rhealth: %d,hunger: %d,sleep: %d,food: %d,potions: %d,money: %d\n",L,H,S,F,P,M)){
        x(s,9,S=9;S)
        x(p,P+8,P--;L+=18;L)
        x(f,H,F--;H)
        x(b,M,P++;M)
        x(t,M,F++;M)
    }
}
Rayo
fuente
2

PHP, 396413 bytes

(Dang, mi primera entrada de código de golf que tuve que editar en el recuento de bytes. Editado para eliminar la llamada sleep (), ya que en realidad no cumplía con las reglas según lo previsto).

Requiere un sistema operativo unix-ish para el STDIN sin bloqueo. Curiosamente, el uso de switch / case versus en cascada si / else produjo un código fuente más corto, pero la versión comprimida posterior fue más larga.

<?eval(gzinflate(base64_decode('bY5BT8MwDIXv/Ao0WWujtVJ7YmtITwiNAwhp3KutOE20pImWVGxa999J1IEAcbH87O892wlUqsEjtmnivD/d5rLd9qZPCHX+gFvdOPTNTpl2L/su3bw9PL1kBaEwMHAMLCsocFaGKhjo0BT0Q0iFaUnO4JmXGlPyPZI8TWHPeIe+nbIIGccJqsGTXbi4p4EKEEt4Qs6xH+rlfA6c5DnwEYacregFlcMvziUk/FLQnzN79drosiOIxV/X7kroeklAh9BxsQD7m/H/MXxi4iKoob5bxRuCTtpFHd8Jn8ab0S7iLOz0pO5LgkfpQ0wrzGyNW+VFBSJ7Nj2eKtDZozHvFfBsPfQdHioYso1CtBW47NV4aXpXgb2Z0csn')));

Sin golf:

<?
shell_exec('stty -icanon');
stream_set_blocking(STDIN, 0);
$u = $s = $p =0;
$f = 10;
$h = $m = 100;
while(1) {
    $t=time();
    while(1)
        if (($k = fgetc(STDIN)) || time() > $t)
            break;
    if ($k == 'f'){
        if ($u > 8 && $f) --$f | $u -= 9;
    } elseif ($k == 's')
        $s = 0;
    elseif ($k == 'p') {
        if ($p) --$p | $h += 9;
    } elseif ($k == 'b') {
        if ($m > 8) $m -= 9 | ++$p;
    } elseif ($k == 't') {
        if ($m > 8) $m -= 9 | ++$f;
    } else {
        if (++$u > 79) --$h;
        if (++$s > 79) $s = 0 | $u += 9;
        if ($h > 79) ++$m;
        if ($h < 1) exit;
    }
    echo"Health:$h,Money:$m,Food:$f,Hunger:$u,Sleep:$s,Potions:$p\n";
}
Alex Howansky
fuente
Esto no "responde inmediatamente a las siguientes pulsaciones de teclas por parte del usuario". Espera hasta que la llamada sleep(1)regrese antes de procesar la entrada. Aunque procesa cualquier comando en cola antes de hacer la próxima actualización, podría estar bien.
Ray
Sí, notado La redacción allí era muy vaga, así que me tomé un poco de libertad en la interpretación porque este camino era más corto. :) Si tengo tiempo hoy, codificaré la solución alternativa con un bucle temporizado y presentaré ambos para su consideración.
Alex Howansky
2

Mathematica, 374 bytes

h=m=100;f=10;g=s=p=0;RunScheduledTask[g++;s++;If[g>79,h--];If[s>79,s=0;g+=9];If[h>79,m++];If[h<1,Quit[]]];Dynamic@Row[{EventHandler[InputField[],"KeyDown":>Switch[CurrentValue@"EventKey","f",If[g>8&&f>0,f--;g-=9],"s",s=0,"p",If[p>0,p--;h+=9],"b",If[m>8,m-=9;p++],"t",If[m>8,m-=9;f++]]],"
Health: ",h,"
Money: ",m,"
Food: ",f,"
Hunger: ",g,"
Sleep: ",s,"
Potions: ",p}]

Los saltos de línea son importantes porque son caracteres de nueva línea en la cadena, por lo que podría usarlos en Rowlugar de Column. Si evalúa esto en un cuaderno de Mathematica, debería ver algo como esto:

Simulación Tamagotchi

Debe hacer clic en el campo de entrada y escribir rápidamente (menos de un segundo) su carácter antes de Dynamicque el campo de entrada se actualice. Este dolor de cabeza podría evitarse por completo si EventHandlerestuviera en su propia celda en lugar de ser un elemento de Row, pero eso requeriría guardar el programa como un archivo .nb que aumentaría en gran medida el recuento de bytes.

ngenisis
fuente
¡Buen trabajo! Iba a usar una estructura como CurrentValue[EvaluationNotebook[], NotebookEventActions] = {"KeyDown" :> Switch[CurrentValue@"EventKey", "f", If[g > 8 && f > 0, f--; g -= 9], "s", s = 0, "p", If[p > 0, p--; h += 9], "b", If[m > 8, m -= 9; p++], "t", If[m > 8, m -= 9; f++]]};... esto debería permitirle evitar la necesidad de hacer clic en un campo de entrada. Agregar la opción , PassEventsDown -> Trueal final de ese código le permitirá continuar editando el cuaderno, pero se puede eliminar al final para ahorrar bytes :)
Greg Martin
¡Gracias, esa es exactamente la funcionalidad que estaba buscando inicialmente! Desafortunadamente parece que es más largo de lo que tengo actualmente.
ngenisis
2

C # 6, 567 563 bytes

using System;using System.Threading;class T{int L,M,F=10,H,S,P;static void Main(){T t=new T();}T(){M=L=100;var W=new Thread(new ThreadStart(Q));W.Start();while(1>0){var r=Console.Read();bool B=0>1,K=1>0;if(r=='f'&H>8&F>0){F--;H-=9;B=K;}if(r=='s'){S=0;B=K;}if(r=='p'&P>0){P--;L+=9;B=K;}if(r=='b'&M>8){M-=9;P++;B=K;}if(r=='t'&M>8){M-=9;F++;B=K;}if(B)p();}}void Q(){while(1>0){H++;S++;if(H>79)L--;if(S>79){S=0;H+=9;}if(L>79)M++;L*=L/L;p();Thread.Sleep(500);}}void p(){Console.Write($"\nhealth: {L}\nhunger: {H}\nsleep: {S}\nfood: {F}\nmoney: {M}\npotions: {P}\n");}}

Sin golf:

using System;
using System.Threading;
class T
{
    int L,M,F=10,H,S,P;
    static void Main()
    {
        T t=new T();
    }
    T()
    {
        M=L=100;
        var W=new Thread(new ThreadStart(Q));
        W.Start();
        while(1>0)
        {
            var r=Console.Read();
            var B=0>1;
            if(r=='f'&H>8&F>0){F--;H-=9;B=1>0;}
            if(r=='s'){S=0;B=1>0;}
            if(r=='p'&P>0){P--;L+=9;B=1>0;}
            if(r=='b'&M>8){M-=9;P++;B=1>0;}
            if(r=='t'&M>8){M-=9;F++;B=1>0;}
            if(B)p();
        }
    }
    void Q()
    {
        while(1>0)
        {
            H++;S++;
            if(H>79)L--;
            if(S>79){S=0;H+=9;}
            if(L>79)M++;
            L*=L/L;
            p();
            Thread.Sleep(500);
        }
    }
    void p()
    {
        Console.Write($"\nhealth: {L}\nhunger: {H}\nsleep: {S}\nfood: {F}\nmoney: {M}\npotions: {P}\n");
    }
}
Yodle
fuente
1

Clojure, 1224 702 bytes

V2

Hizo que todos los átomos perdieran variables en lugar de estar dentro de un objeto de estado. Solo eso eliminó mucho código. También creé las funciones de acceso directo a!y s!para sumar y restar de lo atomsmás fácil (básicamente actuando como +=y -=, ya que Clojure no tiene esos operadores).

Me di cuenta de que probablemente podría acabar con atoms si logro integrar la entrada clave en a loop. Tendré que ver

(ns bits.golf.pet.v2.petms)(def h(atom 100))(def j(atom 0))(def s(atom 0))(def f(atom 10))(def p(atom 0))(def m(atom 100))(defn a[sa n](swap! sa #(+ % n)))(defn v[sa n](swap! sa #(- % n)))(defn c[](a j 1)(a s 1)(if(>=@j 80)(v h 1))(if (>=@s 80)(do(reset! s 0)(a j 9)))(if(>= @h 80)(a m 1)))(defn l[k](case k\f(if(> @j 8)(do(v f 1)(v j 9)))\s(reset! s 0) \p(if(>@p 0)(do(v p 1)(a h 9)))\b(if(> @m 8)(do(v m 9)(a p 1)))\t(if(>@m 8)(do(v m 9)(a f 1)))nil))(defn b[](.start(Thread.^Runnable(fn[](while(>@h 0)(l(first (read-line))))))))(defn -main[](b)(while(>@h 0)(Thread/sleep 500)(c)(println(str"Health: "@h"\nHunger: " @j"\nSleep: "@s"\nFood: "@f"\nPotions: "@p"\nMoney:"@m"\n")))(println"You died!\n"))

Sin golf:

(ns bits.golf.pet.v2.pet)

; 100 0 0 10 0 100
(def he (atom 100))
(def hu (atom 0))
(def sl (atom 0))
(def fo (atom 10))
(def po (atom 0))
(def mo (atom 100))

(defn a! [sa n]
  (swap! sa #(+ % n)))

(defn s! [sa n]
  (swap! sa #(- % n)))

(defn apply-rules []
    (a! hu 1)
    (a! sl 1)
    (if (>= @hu 80)
      (s! he 1))
    (if (>= @sl 80)
      (do
        (reset! sl 0)
        (a! hu 9)))
    (if (>= @he 80)
      (a! mo 1)))

(defn handle-keypress [k]
    (case k
      \f (if (> @hu 8)
           (do
             (s! fo 1)
             (s! hu 9)))
      \s (reset! sl 0)
      \p (if (> @po 0)
           (do
             (s! po 1)
             (a! he 9)))
      \b (if (> @mo 8)
           (do
             (s! mo 9)
             (a! po 1)))
      \t (if (> @mo 8)
           (do
             (s! mo  9)
             (a! fo 1)))
      nil))


(defn start-listener []
  (.start
    (Thread. ^Runnable
      (fn []
        (while (> @he 0)
            (handle-keypress (first (read-line))))))))

(defn -main []
  (start-listener)
  (while (> @he 0)
    (Thread/sleep 500)

    (apply-rules)

    (println (str
               "Health: " @he "\n"
               "Hunger: " @hu "\n"
               "Sleep: " @sl "\n"
               "Food: " @fo "\n"
               "Potions: " @po "\n"
               "Money:" @mo "\n")))

  (println "You died!\n"))

V1

Oh Dios mio. Definitivamente hay margen de mejora aquí. Este tipo de problema es más fácil de hacer con los efectos secundarios, y Clojure es funcional, por lo que estoy tratando de abusar de atoms para disminuir la cantidad de código necesario. Desafortunadamente, no entré con un plan, por lo que es un poco azaroso en este momento. Sin embargo, ya tengo algunas ideas para reducir.

Es un programa completo. Se puede ejecutar ejecutando -main.

(ns bits.golf.pet)(defrecord S[he hu sl fo po mo])(def new-state(->S 100 0 0 10 0 100))(def state(atom new-state))(defn update![sa k f](swap! sa #(update % k f)))(defn apply-rules[s](let [s' (atom s)u! #(update! s' %1 %2)g #(get @s' %)](u! :hu inc)(u! :sl inc)(if(>=(g :hu)80)(u! :he dec))(if(>= (g :sl)80)(do(u! :sl (fn[_]0))(u! :hu #(+ % 9))))(if(>=(g :he)80)(u! :mo inc))@s'))(defn get-input [](let [raw (read-line)](first raw)))(defn handle-keypress[s k](let [s'(atom s)u! #(update! s' %1 %2)g #(get @s' %)](case k\f(if (> (g :hu)8)(do(u! :fo dec)(u! :hu #(- % 9))))\s(u! :sl (fn [_] 0))\p(if(> (g :po)0)(do(u! :po dec)(u! :he #(+ % 9))))\b(if(>(g :mo))(do(u! :mo #(- % 9))(u! :po inc)))\t(if(>(g :mo)8)(do(u! :mo #(- % 9))(u! :fo inc)))nil@s')))(defn start-listener[](.start(Thread.^Runnable(fn[](while true(let[k(get-input)](swap! state #(handle-keypress % k))))))))(defn -main[](start-listener)(let[g #(get @%1 %2)](while true(Thread/sleep 500)(swap! state #(apply-rules %))(println(str"Health: "(g state :he)"\nHunger: "(g state :hu)"\n""Sleep: " (g state :sl)"\nFood: "(g state :fo)"\nPotions: "(g state :po)"\n""Money:"(g state :mo)"\n"))(if(<=(g state :he)0)(do(println"You died!\n")(reset! state new-state))))))

Sin golf:

(ns bits.golf.pet)

(defrecord State [he hu sl fo po mo])

(def new-state (->State 100 0 0 10 0 100))

(def state (atom new-state))

(defn update! [sa k f]
  (swap! sa #(update % k f)))

(defn apply-rules [s]
  (let [s' (atom s)
        u! #(update! s' %1 %2)
        g #(get @s' %)]
    (u! :hu inc)
    (u! :sl inc)
    (if (>= (g :hu) 80)
      (u! :he dec))
    (if (>= (g :sl) 80)
      (do
        (u! :sl (fn [_] 0))
        (u! :hu #(+ % 9))))
    (if (>= (g :he) 80)
      (u! :mo inc))
    @s'))

(defn get-input []
  (let [raw (read-line)]
    (first raw)))

(defn handle-keypress [s k]
  (let [s' (atom s)
        u! #(update! s' %1 %2)
        g #(get @s' %)]
    (case k
      \f (if (> (g :hu) 8)
           (do
             (u! :fo dec)
             (u! :hu #(- % 9))))
      \s (u! :sl (fn [_] 0))
      \p (if (> (g :po) 0)
           (do
             (u! :po dec)
             (u! :he #(+ % 9))))
      \b (if (> (g :mo))
           (do
             (u! :mo #(- % 9))
             (u! :po inc)))
      \t (if (> (g :mo) 8)
           (do
             (u! :mo #(- % 9))
             (u! :fo inc)))
      nil
      @s')))

(defn start-listener []
  (.start
    (Thread. ^Runnable
      (fn []
        (while true
          (let [k (get-input)]
            (swap! state #(handle-keypress % k))))))))

(defn -main []
  (start-listener)
  (let [g #(get @%1 %2)]
    (while true
      (Thread/sleep 500)

      (swap! state #(apply-rules %))

      (println (str
                 "Health: " (g state :he) "\n"
                 "Hunger: " (g state :hu) "\n"
                 "Sleep: " (g state :sl) "\n"
                 "Food: " (g state :fo) "\n"
                 "Potions: " (g state :po) "\n"
                 "Money:" (g state :mo) "\n"))

      (if (<= (g state :he) 0)
        (do
          (println "You died!\n\n\n\n\n")
          (reset! state new-state))))))
Carcigenicate
fuente
Esa es una buena cantidad de código allí.
ender_scythe
@ender_scythe Ya. Intenté adherirme a prácticas de codificación medio decentes en algunos lugares, como hacer applying-rulesy handle-keypresspure. Estoy escribiendo una versión "atornilla" en este momento.
Carcigenicate