Mathematica, 100%, 141 bytes
f@x_:=Count[1>0]@Table[ImageInstanceQ[x,"caprine animal",RecognitionThreshold->i/100],{i,0,50}];If[f@#>f@ImageReflect@#,"Up","Down"]<>"goat"&
Well, this feels more than a little like cheating. It's also incredibly slow as well as being very silly. Function f
sees roughly how high you can set the Recognition threshold in one of Mathematica's computer vision builtins, and still recognise the image as a Caprine animal.
We then see whether the image or the flipped image is more goaty. Works on your profile image only because tie is broken in favour of downgoat. There are probably loads of ways this could be improved including asking it if the image represents Bovids or other generalisations of the Caprine animal entity type.
Answer as written scores 100% for the first testing set and 94% for the second testing set, as the algorithm yields an inconclusive result for goat 1. This can be raised back up to 100% at the expense of an even longer computational time by testing more values of RecognitionThreshold
. Raising from 100
to 1000
sufficies; for some reason Mathematica thinks that's a very ungoaty image! Changing the recognition entity from Caprine animal to Hoofed Mammal also seems to work.
Ungolfed:
goatness[image_] := Count[
Table[
ImageInstanceQ[
image, Entity["Concept", "CaprineAnimal::4p79r"],
RecognitionThreshold -> threshold
],
{threshold, 0, 0.5, 0.01}
],
True
]
Function[{image},
StringJoin[
If[goatness[image] > goatness[ImageReflect[image]],
"Up",
"Down"
],
"goat"
]
]
Alternative solution, 100% + bonus
g[t_][i_] := ImageInstanceQ[i, "caprine animal", RecognitionThreshold -> t]
f[i_, l_: 0, u_: 1] := Module[{m = (2 l + u)/3, r},
r = g[m] /@ {i, ImageReflect@i};
If[Equal @@ r,
If[First@r, f[i, m, u], f[i, l, m]],
If[First@r, "Up", "Down"] <> "goat"
]
]
This one uses the same strategy as before, but with a binary search over the threshold. There are two functions involved here:
g[t]
returns whether or not its argument is a goaty image with threshold t
.
f
takes three parameters: an image, and an upper and lower bound on the threshold. It is recursive; it works by testing a threshold m
between the upper and lower thresholds (biased towards the lower). If the image and the reflected image are both goaty or non-goaty, it eliminates the lower or upper part of the range as appropriate and calls itself again. Otherwise, if one image is goaty and the other is non-goaty, it returns Upgoat
if the first image is goaty and Downgoat
otherwise (if the second, reflected image is goaty).
The function definitions deserves a little explanation. First, function application is left-associative. This means that something like g[x][y]
is interpreted as (g[x])[y]
; "the result of g[x]
applied to y
."
Second, assignment in Mathematica is roughly equivalent to defining a replacement rule. That is, f[x_] := x^2
does not mean "declare a function named f
with parameter x
that returns x^2
;" its meaning is closer to, "whenever you see something like f[ ... ]
, call the thing inside x
and replace the whole thing with x^2
."
Putting these two together, we can see that the definition of g
is telling Mathematica to replace any expression of the form (g[ ... ])[ ... ]
with the right-hand side of the assignment.
When Mathematica encounters the expression g[m]
(in the second line of f
), it sees that the expression does not match any rules that it knows and leaves it unchanged. Then it matches the Map
operator /@
, whose arguments are g[m]
and the list {i, ImageReflect@i}
. (/@
is infix notation; this expression is exactly equivalent to Map[g[m], { ... }]
.) The Map
is replaced by applying its first argument to each element of its second argument, so we get {(g[m])[i], (g[m])[ ... ]}
. Now Mathematica sees that each element matches the definition of g
and does the replacement.
In this way we got g
to act like a function that returns another function; that is, it acts roughly like we wrote:
g[t_] := Function[{i}, ImageInstanceQ[i, "caprine animal", RecognitionThreshold -> t]]
(Except in this case g[t]
on its own evaluates to a Function
, whereas before g[t]
on its own was not transformed at all.)
The final trick I use is an optional pattern. The pattern l_ : 0
means "match any expression and make it available as l
, or match nothing and make 0
available as l
." So, if you call f[i]
with one argument (the image to test) it is as if you had called f[i, 0, 1]
.
Here is the test harness I used:
gist = Import["https://api.github.com/gists/3fb94bfaa7364ccdd8e2", "JSON"];
{names, urls} = Transpose[{"filename", "raw_url"} /. Last /@ ("files" /. gist)];
images = Import /@ urls;
result = f /@ images
Tally@MapThread[StringContainsQ[##, IgnoreCase -> True] &, {names, result}]
(* {{True, 18}} *)
user = "items" /.
Import["https://api.stackexchange.com/2.2/users/40695?site=codegolf", "JSON"];
pic = Import[First["profile_image" /. user]];
name = First["display_name" /. user];
name == f@pic
(* True *)