10, 10, 10… mam nadzieję?

15

Przedmowa

Kiedy strzelałem dziś z łucznictwa 900 rundę wcześniej (10 kończy się na 6 strzałach na końcu i 10 kończy się na 3 strzałach na końcu, w sumie 90 strzał i maksymalny wynik 900), pomyślałem o tym wyzwaniu.

W strzelaniu z łuku (zakładając, że strzelasz do tarczy dostarczonej przez FITA [kawałek papieru, do którego strzelasz]), za każdą strzałę możesz zdobyć maksymalną liczbę punktów 10. Tarcza docelowa zawiera 10 lub 11 pierścieni o malejącej średnicy, zagnieżdżone w sobie. Od wewnętrznego pierścienia na zewnątrz są one liczone od 10 punktów do jednego punktu (aw przypadku 11 pierścieni istnieje drugi pierścień wewnętrzny, który liczy się jako „X”, który otrzymuje wynik 10, ale jest stosowany w przypadkach zerwania remisu jako wyższa wartość). Przestrzegać:

Punktacja celów FITA

Oczywiście mam na myśli punktację FITA Metric, jak widać na powyższej ilustracji. Jeśli przyjrzysz się uważnie, możesz zaobserwować najbardziej wewnętrzny pierścień, który jest wyblakłą przerywaną linią, którego wynik nie jest zaznaczony. To jest „X”, o którym mówiłem, ale nie będziesz musiał tego słuchać, chyba że będziesz walczył o bonus.

Wyzwanie

Utwórz funkcję (lub pełny program, jeśli język nie obsługuje funkcji), który otrzymuje idealnie kwadratowy obraz jako dane wejściowe (lub nazwę pliku obrazu, jeśli to konieczne), zawierający pewną liczbę zieleni (HEX # 00FF00, RGB (0, 255, 0)) kropek o pewnym rozmiarze i zwraca wynik. Obraz może zawierać dane inne niż zielone kropki , ale zielony zawsze będzie miał dokładnie ten sam odcień.

Możesz sobie wyobrazić, że kwadratowy obraz przedstawia twarz docelową, z zewnętrznym pierścieniem dotykającym 4 punktów (górny środek, dolny środek, prawy środek, lewy środek). Reprezentowana powierzchnia docelowa zawsze będzie miała tę samą proporcję, przy czym wszystkie pierścienie będą miały szerokość dokładnie 1/20 szerokości wejściowego obrazu docelowego. Jako przykład, biorąc pod uwagę obraz wejściowy o wymiarach wejściowych 400px na 400px, możesz założyć, że każdy pierścień ma wewnętrzną szerokość 20px, jak pokazano poniżej:

Przykra przykładowa ilustracja

Wyjaśnienia

  • Po dotknięciu dwóch oddzielnych pierścieni liczony jest wyższy z dwóch pierścieni
  • Nie musisz automatycznie rozliczać się z braków lub przypadku „x”, chyba że próbujesz uzyskać bonus
  • Możesz założyć, że żadne zielone kółka się nie pokrywają
  • Możesz również założyć, że na obrazie nie ma innych pikseli tego odcienia zieleni
  • Obraz będzie w formacie PNG, JPEG lub PPM (do wyboru)
  • Zewnętrzne biblioteki przetwarzania obrazu są dozwolone, jeśli zostały utworzone przed opublikowaniem tego pytania
  • Możesz założyć, że wszystkie zielone kółka na jednym celu będą miały tę samą średnicę
  • Jeśli strzelasz (hah) w celu uzyskania premii za nakładające się koła, możesz założyć, że co najmniej jedno koło na obrazie nie ma innego nakładającego się koła
  • Standardowe luki są niedozwolone

Przypadki testowe

Poniższe dwa przypadki powinny uzyskać 52 (lub w przypadku bonusów 52 z 1 'x' i 1 chybcem):

Ten ostatni przypadek testowy powinien uzyskać 25 :

Premia

  • -25 bajtów, jeśli również zwrócisz liczbę braków (poza dowolnym pierścieniem)
  • -30 bajtów, jeśli zwrócisz również liczbę Xs (załóż, że najbardziej wewnętrzny x to 3/100 szerokości obrazu, a 10 to 2/100 szerokości obrazu. Proporcje 1-9 pozostają niezmienione)
  • -35% liczby bajtów, jeśli uwzględnisz nakładające się kręgi

To jest kod golfowy, więc wygrywa najmniej bajtów. Baw się dobrze!

globby
źródło
„30 kończy się na 3 strzały na końcu, w sumie 30 strzał”? Czy nie powinno to być 90 strzał?
DavidC
@DavidCarraher Zdałem sobie sprawę, że tak, jak pisałem. Poprawiono
globby
Jakich formatów obrazów możemy użyć? PNG? PPM? Nasz własny niestandardowy format? (Zakładam, że pierwsze dwa, ale nie trzeci, ale tylko dla wyjaśnienia.)
Klamka
Powiedzmy po prostu JPEG lub PNG dla uproszczenia @Doorknob 冰
globby
1
Myślę, że najtrudniejszy bonus to ten z najmniejszą nagrodą.
Justin

Odpowiedzi:

4

Przetwarzanie 2, 448–25 = 423 bajtów

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;}

Wczytuje plik obrazu f, który przechodzi przez piksele, aż znajdzie zielony kolor, a następnie zalanie wypełnia okrąg określający punkt, który jest najbliżej środka. Następnie dodaje ten wynik do sumy. jeśli wynik jest ujemny, jest dodawany do licznika braków.

Program wyświetli 2 liczby, pierwsza to wynik, a druga liczba braków.

  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;
}

możesz uzyskać przetwarzanie tutaj

bubalou
źródło
4

Perl 5 + GD: 225-25 = 200

Edycja: Znalazłem przyczynę nieprawidłowego odczytu pikseli w zindeksowanych plikach PNG i zastosowałem obejście. Z jakiegoś powodu w bibliotece GD wartości zielonych pikseli są odczytywane jako (4,254,4). Nie jestem pewien, czy dotyczy to plików PNG zawartych w pytaniu. Podziały linii można usunąć w poniższym kodzie.

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 $%"

Pobiera obraz PNG na wejściu i drukuje 2 wartości: Liczba punktów i braków. Na przykład:

perl arch.pl <arch52.png
52 1

Zmiana w ostatniej chwili:

W prawdziwym trybie kolorów, którego i tak potrzebowałem, indeksy kolorów używane getPixeli fillsą po prostu liczbami RGB zakodowanymi liczbami całkowitymi, więc nie trzeba używać rgbicolorAllocate konwertować na te indeksy i z nich.

Wyjaśnienie:

  • Wygeneruj listę wszystkich współrzędnych pikseli (jako pary liczb całkowitych oddzielonych spacjami).
  • Sortuj według potencjalnego wyniku (za pomocą sub vktórego przeprowadzany jest parametr$_ zamiast parametrów standardowych, ponieważ jest on krótszy).
  • Dla każdego piksela, zaczynając od tych o najwyższym wyniku, jeśli jest zielony, dodaj do wyniku i wypełnij swoją lokalizację czarnym kolorem.
nutki
źródło
To nie są obrazy; Odpowiedź @ bubalou poprawnie odczytuje kolory jako # 00FF00
globby
@globby Wiem, że kolory są poprawne na obrazie (sprawdziłem za pomocą oprogramowania do edycji obrazu), ale być może są też te same meta informacje, aby przyciąć przestrzeń kolorów.
nutki
możliwie. To dziwne
globby
3

Haskell - 579-25 = 554 603-25-30 576-25-30 = 521 bajtów

Strategia:

  • Zrób listę (d, x, y) potrójnych dla wszystkich pikseli (d to odległość od środka)
  • posortuj listę według odległości
  • zaczynając od największej odległości: jeśli piksel jest jedynym zielonym pikselem w małym sąsiedztwie, zachowaj jego odległość na liście L, w przeciwnym razie zaczernij go
  • obliczyć wynik z listy odległości L.

Wyjście jest potrójne (wynik, chybienie, Xs), np (52,1,1) Dla obrazu testowego.

Program może zawieść, jeśli piksel koła najbliższego środka znajduje się w odległości 3 pikseli od innego koła.

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
źródło
Wskazówka: all idjest taka sama jak. and, A także można wdrożyć za jpomocą strażników wzorówj n m|PixelRGBA8 0 255 0 _<-getColor n m v=0<1|0<1=0>1
dumny haskeller
@proudhaskeller: Tak, dziękuję!
nimi
2

Mathematica - 371 386 - 25 = 361

Bardziej optymalne rozwiązanie. Oblicza odpowiedź znacznie szybciej niż moje rozwiązanie 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 z PIL - trywialne i nieoptymalne rozwiązanie, 961 bajtów

Jest to po prostu próba wykazania głupiego podejścia do rozwiązania problemu. Uruchomienie dwóch pierwszych przypadków testowych zajmuje ~ 2 minuty, a uruchomienie trzeciego w moim systemie zajmuje ~ 20 minut z powodu szybko wykonanego, strasznie wymagającego zasobów i odpychająco złożonego algorytmicznie detektora okręgu. Mimo to spełnia wymagania, choć z pewnością nie jest optymalnie golfowy. Im więcej zieleni na obrazie, tym dłużej trwa.

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

Pobiera obiekt obrazu PIL i zwraca wynik.

Kroki, które trzeba podjąć:

  1. Izoluj zielone kółka (nieefektywnie)
    • Znajdź wszystkich sąsiadów jakiegoś piksela n , jeśli są to zielone piksele, następnie dodaj je do okręgu
    • Określ szorstki kontur, odfiltrowując piksele, które mają 8 sąsiadów
  2. Narysuj reprezentację docelową
    • Utwórz puste płótno
    • Narysuj unikatowe kolorowe tło (łatwe do wdrożenia)
    • Narysuj zagnieżdżone elipsy w unikalnych kolorach
  3. Określ strefy punktacji, w których znajduje się każde koło, określając kolor (y) celu, który byłby pod kołem
  4. Wybierz wyższą ze stref punktacji (jeśli jest ich wiele) i dodaj wynik do sumy
  5. Zwróć sumę
globby
źródło
Twoja funkcja w Pythonie amoże być zapisana jakoa=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]
ov