"You must construct additional pylons!"
Python 3, 207*.9 == 186.3 bytes.
Implements the nexus bonus.
Saved 26 bytes thanks to DSM.
Saved 2 bytes thanks to Tim Pederick
x,*l=input().split()
d=-((int(x)*8-sum((('vexuobcl'+2*'clsuie'+4*'Ratahoiesuhihi').count(q[-3:-1])*(1-12*(q[0]>'w'))or 2)for q in l))//8)
print((1,"You must construct %s additional pylon"%d+'s!'[d<2:])[d>0])
JavaScript, 274 265 bytes (no bonuses) 281 - 10% = 252.9 bytes
s=>(c=eval(s[a="replace"](" ","-(")[a](/ /g,"+")[a](/\w+/g,m=>m[b="match"](/^(Pr|Ob)/g)?1:m[b](/^([ZSHDWP]|M\w+C)/g)?2:m[b](/^O/g)?3:m[b](/^[IAVT]/g)?4:m[b](/^N/g)?-11:m[b](/^C/g)?6:+m!=m?8:m*8)+")"))>0?true:`You must construct ${Math.ceil(-c/8)} additional pylon${c/8<-1?"s":""}!`
This appears to be pretty lengthy...
Demo + explanation:
p = s => (c = eval(
s[a = "replace"](" ", "-(") //replace the first space (after the number of pylons) with "-" and open the bracket
[a](/ /g, "+") //replace all the remaining spaces with "+"
[a](/\w+/g, m => //replace any sequence of characters with...
m[b = "match"](/^(Pr|Ob)/g) ? 1 : //with 1, if matches "Probe" or "Observer"
m[b](/^([ZSHDWP]|M\w+C)/g) ? 2 : //with 2, if it matches bunch of the units with cost 2. "Probe" didn't match already, so it's safe to catch "Phoenix" by only the first letter.
m[b](/^O/g) ? 3 : //with 3, if match is "Oracle"
m[b](/^[IAVT]/g) ? 4 : //with 4, if matches "Immortal", "Archon", "VoidRay" or "Tempest"
m[b](/^C/g) ? 6 : //with 6, if it's "Carrier" or "Colossus"
m[b](/^N/g) ? -11 : //don't forget nexuses!
+m != m ? 8 : m * 8 //and if's not a number, then it's "Mothership", so with 8. If it's number, it can only be the number of pylons, replace it with itself multiplied by 8.
) + ")" //close the opened bracket
)) > 0 ? true : `You must construct ${Math.ceil(-c/8)} additional pylon${c/8<-1?"s":""}!`
document.write(
p("2 Probe Probe Probe Probe Stalker Zealot Carrier Probe Zealot") + "<br>" +
p("5 Mothership Carrier Probe Tempest HighTemplar") + "<br>" +
p("0 Mothership Colossus Zealot") + "<br>" +
p("0 Probe Nexus Probe")
)
Python 3, 293 − 30% = 205.1 bytes
Implements both bonuses. Prints 1 as its truthy value, and either 0 or an empty string as its falsey value.
s='5N 8O5P bDbHeM7P6S7S9W6Z 6O 6A8I7T7V . 7C8C . aM'.split()
m=M=n=o=0
p,*u=input().split()
for v in u:
S=0
while'%x'%len(v)+v[0]not in s[S]:S+=1
n+=S or-11;M+=S>7;m+='pC'in v;o+=m>1or M>1or m<1<=M
q=int(p)+n//-8
print([1,'You must construct %d additional pylon'%-q+'s!'[q>-2:]][q<0]*(o<1))
Credit to Dom Hastings' solution for helping me shave off a good few bytes with a "poor man's ceil
" of my own, and Morgan Thrapp's for the idea underlying 's!'[q>-2:]
, which saved me six bytes—not to mention pointing out, in the comments, how to save another byte on that bit.
Explanations
The string in line 1 encodes all of the units and their supply requirements. Each unit is represented as two characters: a hexadecimal digit giving the length of the unit's name, and the first character of the name (e.g. 8O
is the Observer; aM
is the Mothership). The supply requirement is the index of the encoded unit within the sequence s
, formed by splitting the string on the spaces. Full stops mark unused amounts of supply (no unit needs 5 or 7 supply), and as a special case, the Nexus (5N
) is at index 0.
Line 2 initialises values: m
is the number of mothership cores, M
is the number of motherships, n
is the total supply cost, and o
indicates whether or not the mothership build conditions have been violated. Line 3 takes the input, putting the number of pylons into p
and the list of units into u
.
Within the loop that starts at line 4, S
is an index into s
and, thus, also the amount of supply needed for the current unit, v
. In line 6, the while
statement steps through s
until the unit is found. ('%x'%len(v)
turns the length of the unit's name into a hex digit.)
Line 7 updates the total supply cost n
(note the special case, -11
, if S
is zero). It then increments the counts of motherships M
(identified by the supply cost being over 7) and mothership cores m
(identified by the substring pC
in the unit's name). Then, if either of these is greater than 1, or if M
is at least one while m
is still zero, the flag o
is set. (Actually, it's incremented, but later on we only care if it's zero or non-zero.)
The pylon deficit q
is calculated, a little oddly, in line 8. Naively, it should be n // 8 - int(p)
(i.e. one eighth of the supply cost, minus any pylons we already have). But that would round down, when we need to round up. Integer division (//
) rounds towards negative infinity, though, so we just work everything in negatives: int(p) - -(n // -8)
, which simplifies to the form actually used.
Lastly, the output. If we're just one pylon short, q
will be -1, hence the test q>-2
(which slices the s
out of the string s!
if true, and keeps it otherwise). If there's no pylon deficit, q
will be zero or positive, hence q<0
to select between the truthy value 1 or the output string. Lastly, if the flag o
isn't zero, multiplying either result (1 or a string) by Boolean False
(handled as numeric 0) will give a falsey value (0 or the empty string).