10, 10, 10 ... espero?

15

Prefacio

Como estaba disparando un tiro de arco 900 hoy más temprano (10 termina con 6 flechas por final, y 10 termina con 3 flechas por final, para un total de 90 flechas y un puntaje máximo de 900), pensé en este desafío.

En tiro con arco (suponiendo que está disparando en una cara objetivo suministrada por FITA [la hoja de papel a la que dispara]), por cada flecha puede reclamar una puntuación máxima de 10. La cara objetivo contiene 10 u 11 anillos de diámetro decreciente, anidados uno dentro del otro. Desde el anillo interno hacia afuera, estos se cuentan de 10 puntos a un punto (y en el caso de 11 anillos, hay un anillo interno más secundario que cuenta como 'X', que puntúa como 10 pero se usa en casos de desempate como El valor más alto). Observar:

FITA Target Scoring

Por supuesto, me refiero a la puntuación métrica FITA, como se ve en la ilustración anterior. Si observa de cerca, puede observar el anillo más interno, que es una línea de puntos desvanecida, cuyo puntaje no está marcado. Esa es la 'X' a la que me refería, pero no tendrá que prestar atención a eso a menos que compita por el bono.

Desafío

Cree una función (o programa completo, si el idioma no admite funciones), que reciba una imagen perfectamente cuadrada como entrada (o nombre de archivo de imagen, si es necesario), que contenga algún número de verde (HEX # 00FF00, RGB (0, 255, 0)) puntos de algún tamaño, y devuelve la puntuación. La imagen puede contener datos distintos de los puntos verdes , pero el verde siempre será exactamente el mismo tono.

Puede imaginar que la imagen cuadrada representa la cara del objetivo, con el anillo más externo tocando en 4 puntos (centro superior, centro inferior, centro derecho, centro izquierdo). La cara objetivo representada siempre será de la misma proporción, con todos los anillos con un ancho de exactamente 1/20 del ancho de la imagen objetivo de entrada. Como ejemplo, dada una imagen de entrada de dimensiones de entrada de 400 px por 400 px, puede suponer que cada anillo tiene un ancho interno de 20 px, como se ilustra a continuación:

Ejemplo de ejemplo horrible

Aclaraciones

  • Si toca dos anillos separados, se cuenta el mayor de los dos anillos
  • No tiene que contabilizar automáticamente las fallas o el caso 'x', a menos que intente el bono
  • Puede suponer que no se superponen círculos verdes
  • También puede suponer que no hay otros píxeles de ese tono de verde en la imagen
  • La imagen estará en formato PNG, JPEG o PPM (a elección)
  • Se permiten bibliotecas de procesamiento de imágenes externas, si se crearon antes de publicar esta pregunta
  • Puede suponer que todos los círculos verdes en un objetivo tendrán el mismo diámetro
  • Si disparas (hah) para la bonificación de círculos superpuestos, puedes suponer que al menos un círculo en la imagen no tiene otra superposición
  • Las lagunas estándar no están permitidas

Casos de prueba

Los siguientes dos casos deberían tener un puntaje de 52 (o en el caso de bonos, 52 con 1 'x' y 1 falta):

Y este último caso de prueba debería obtener una puntuación de 25 :

Prima

  • -25 bytes si también devuelve el número de errores (fuera de cualquiera de los anillos)
  • -30 bytes si también devuelve la cantidad de X (suponga que la x más interna es 3/100 del ancho de la imagen, y 10 es entonces 2/100 del ancho de la imagen. Las proporciones 1-9 permanecen sin cambios)
  • -35% de recuento de bytes si tiene en cuenta círculos superpuestos

Este es el código de golf, por lo que gana el menor número de bytes. ¡Que te diviertas!

globby
fuente
"30 termina en 3 flechas por final, para un total de 30 flechas"? ¿No deberían ser 90 flechas?
DavidC
@DavidCarraher Me di cuenta de eso justo cuando publiqué. Corregido
globby
¿Qué formatos de imagen podemos usar? PNG? PPM? Nuestro propio formato personalizado? (Asumiría los dos primeros pero no el tercero, pero solo para aclarar).
Pomo de la puerta
Digamos solo JPEG o PNG para simplificar @Doorknob 冰
globby
1
Creo que el bono más difícil es el que tiene la menor recompensa.
Justin

Respuestas:

4

Procesamiento 2, 448-25 = 423 bytes

int x,y,w,b,v,c,m;color g;PImage i;void setup(){i=loadImage("f.png");w=i.width;size(w,w);g=#00ff00;image(i,0,0);b=v=x=y=c=m=0;loadPixels();while(y*w+x<w*w){if(pixels[y*w+x]==g){f(y,x);if(v>=0)c+=v;else m++;}v=-1;x++;if(x==w){x=0;y++;}}println(c+" "+m);}void f(int k,int l){pixels[k*w+l]=color(0);if(pixels[(k+1)*w+l]==g)f(k+1,l);if(pixels[k*w+l+1]==g)f(k,l+1);if(pixels[k*w+l-1]==g)f(k,l-1);k-=w/2;l-=w/2;b=10-(int)(sqrt(k*k+l*l)/(w/20));if(b>v)v=b;}

Lee en un archivo de imagen f recorre los píxeles hasta que encuentra verde y luego inunda el círculo determinando el punto más cercano al centro. Luego agrega esa puntuación a un total. Si el puntaje es negativo, se agrega a un contador de fallas.

El programa generará 2 números, el primero es el puntaje y el segundo es el número de fallas.

  int x,y,w,b,v,c,m;
  color g;
  PImage i;
void setup()
{
  i=loadImage("f.png");
  w=i.width;
  size(w,w);
  g=#00ff00;
  image(i,0,0);
  b=v=x=y=c=m=0;  
  loadPixels();
  while(y*w+x<w*w)
  {
    if(pixels[y*w+x]==g)
    {
      f(y,x);
      if(v>=0)c+=v;
      else m++;
    }
    v=-1;
    x++;
    if(x==w){x=0;y++;}
  }
  print(c+" "+m);
}

void f(int k,int l)
{
  pixels[k*w+l]=color(0);
 if(pixels[(k+1)*w+l]==g)f(k+1,l);
 if(pixels[k*w+l+1]==g)f(k,l+1);
 if(pixels[k*w+l-1]==g)f(k,l-1); 
 k-=w/2;
 l-=w/2;
 b=10-(int)(sqrt(k*k+l*l)/(w/20));
 if(b>v)v=b;
}

puedes procesar aquí

bubalou
fuente
4

Perl 5 + GD: 225-25 = 200

Editar: Encontró el motivo de la lectura incorrecta de píxeles en PNG indexados y aplicó una solución alternativa. Por alguna razón con la biblioteca GD, los valores de píxeles verdes se leen como (4,254,4). No estoy seguro de si esto es específico para los archivos PNG incluidos en la pregunta. Los saltos de línea se pueden eliminar en el siguiente código.

use GD;$k=newFromPng GD::Image'-',1;
sub v{/ /;10-int((($'-@x/2)**2+($`-@x/2)**2)**.5/@x*20)}
map{v>0?($r+=v):$%++,fill$k @c,0if 65280==getPixel$k @c=split
}sort{v($_=$b)- v$_=$a}map{//;map"$_ $'",@x}@x=0..width$k-1;
print"$r $%"

Toma una imagen PNG en la entrada e imprime 2 valores: Número de puntos y errores. Por ejemplo:

perl arch.pl <arch52.png
52 1

Cambio de última hora:

En el modo de color verdadero que necesitaba de todos modos, los índices de color utilizados por getPixely fillson simplemente valores RGB codificados con enteros, por lo que no es necesario usar rgby colorAllocateconvertir ay desde esos índices.

Explicación:

  • Genere una lista de todas las coordenadas de píxeles (como pares de enteros separados por espacios).
  • Ordene por puntaje potencial (mediante el sub vcual toma el parámetro en $_lugar de los parámetros estándar, ya que es más corto).
  • Para cada píxel a partir de los más altos, si es verde, agregue al resultado e inunde su ubicación en negro.
nutki
fuente
No son las imágenes; La respuesta de @ bubalou leyó correctamente los colores como # 00FF00
globby
@globby Sé que los colores son correctos en la imagen (verifiqué con el software de edición de imágenes), pero tal vez también haya la misma metainformación para recortar el espacio de color.
nutki
posiblemente. Sin embargo, eso es extraño
2014
3

Haskell - 579-25 = 554603-25-30 576-25-30 = 521 Bytes

Estrategia:

  • Haga una lista de (d, x, y) triples para todos los píxeles (d es la distancia al centro)
  • ordenar la lista por distancia
  • comenzando con la mayor distancia: si el píxel es el único píxel verde en un vecindario pequeño, mantenga su distancia en una lista L, de lo contrario ennegrezca
  • calcular la puntuación de la lista de distancia L

La salida es un triple (puntuación, errores, X), por ejemplo, (52,1,1)para la imagen de prueba.

El programa puede fallar si el píxel de un círculo más cercano al centro está dentro de los 3 píxeles de otro círculo.

import Data.List
import Codec.Picture
import Codec.Picture.RGBA8
import Codec.Picture.Canvas
z=fromIntegral
f(e,x,y)(v,h)|and$j x y:[not$j a b|a<-[x-3..x+3],b<-[y-3..y+3],not(a==x&&b==y)]=(v,e:h)|1<2=(setColor x y(PixelRGBA8 0 0 0 0)v,h)
 where j n m|PixelRGBA8 0 255 0 _<-getColor n m v=0<1|0<1=0>1
d k r(h,i,j)|r>10*k=(h,i+1,j)|r<k*3/5=(h+10,i,j+1)|1<2=(10-(floor$r/k)+h,i,j)
main=do
 i<-readImageRGBA8 "p.png"
 let(Right c)=imageToCanvas i;s=canvasWidth c;q=[3..s-4];(_,g)=foldr f(c,[])$sort[(sqrt$(z x-z s/2)^2+(z y-z s/2)^2,x,y)|x<-q,y<-q]
 print$foldr(d$z s/20)(0,0,0)g
nimi
fuente
Un consejo: all ides lo mismo que and.También, puedes implementarlo jcon guardias de patronesj n m|PixelRGBA8 0 255 0 _<-getColor n m v=0<1|0<1=0>1
orgulloso Haskeller
@proudhaskeller: ¡Sí, gracias!
nimi
2

Mathematica - 371 386 - 25 = 361

Una solución más óptima. Calcula la respuesta mucho más rápido que mi solución Python.

i=IntegerPart;c=i/@((NestList[#+.01&,.1,10]~Prepend~1)*100);g[m_]:=Last@@c~Position~#-1&/@(i@Round@Last@#&/@(#*100&/@Riffle[RGBColor/@NestList[#+.01&,{.1,.1,.1},10],Table[Disk[{0,0},n],{n,1,.1,-.1}]]~Graphics~{ImageSize->ImageDimensions[m],PlotRangePadding->None}~ImageMultiply~ChanVeseBinarize[m,"TargetColor"->Green]~ComponentMeasurements~"Max"/.Rule[a_,b_]:>b))//{Total@#,#~Count~0}&

Python con PIL: una solución trivial y no óptima, 961 bytes

Esto es simplemente para tratar de demostrar un enfoque tonto para resolver el problema. Se necesitan ~ 2 minutos para ejecutar los primeros dos casos de prueba, y ~ 20 minutos para ejecutar el tercero en mi sistema debido al detector de círculo rápidamente inventado, terriblemente intensivo en recursos y repulsivamente algorítmicamente complejo. A pesar de esto, cumple con los requisitos, aunque ciertamente no es un campo de golf óptimo. Cuanto más verde hay en la imagen, más tiempo tarda en ejecutarse.

from PIL import Image,ImageDraw
a=lambda x,y,w,h:filter(lambda x:0<=x[0]<w and 0<=x[1]<h,[(x-1,y-1),(x,y-1),(x+1,y-    1),(x-1,y),(x,y),(x+1,y),(x-1,y+1),(x,y+1),(x+1,y+1)])
def b(c):
 d=0,255,0;e,f=c.size;g=c.load();h,i=[],[];j=Image.new("RGB",(e,f));k=ImageDraw.Draw(j)
 for l in range(e):
  for m in range(e):
   n=g[l,m][:-1]
   if n==d and(l,m)not in i:
    o=[(l,m)];p=[];q=1
    while q:
     q=0;r=o[:]
     for s in o:
      t=filter(lambda x:g[x[0],x[1]][:-1]==d and(x[0],x[1]) not in r,a(s[0],s[1],e,f))
      if t:
       r+=t
       if len(t)<8:
        p+=[s]
       q=1
     o=r
    h+=[p]
    for u in o:
     i+=[u]
   i+=[(l,m)]
 p=map(lambda x:"#"+str(x)*6,'123456789ab');v=0;k.rectangle((0,0,e,f),fill=p[0])
 for n in p[1:]:
  w=e/20*v;x=e-w;k.ellipse((w,w,x,x),fill=n);v+=1
 y=j.load();z=0
 for l in h:
  v=[]
  for m in l:
   s=y[m[0],m[1]]
   if s not in v:
    v+=[s]
  v=max(v);z+=p.index("#"+''.join(map(lambda x:hex(x)[2:],v)))
 return z

Toma un objeto de imagen PIL y devuelve la puntuación.

Pasos necesarios:

  1. Aislar círculos verdes (ineficientemente)
    • Encuentre a todos los vecinos de algún píxel n, si alguno es de color verde, agréguelos al círculo.
    • Determine el contorno aproximado filtrando los píxeles que tienen 8 vecinos.
  2. Dibuja una representación objetivo
    • Crea un lienzo en blanco
    • Dibuje un fondo de color único (fallas fáciles de implementar)
    • Dibuja elipses anidadas con colores únicos
  3. Determine en qué zonas de puntuación se encuentra cada círculo determinando los colores del objetivo que estarían debajo del círculo
  4. Elija la mayor de las zonas de puntuación (si es múltiple) y agregue la puntuación al total
  5. Devuelve el total
globby
fuente
Su función Python ase puede escribir comoa=lambda x,y,w,h:[(X,Y)for X in(x-1,x,x+1)for Y in(y-1,y,y+1)if w>X>-1<Y<h]
ovs