Your challenge is to write a polyglot that works in different versions of your language. When run, it will always output the language version.

# Rules

• Your program should work in at least two versions of your language.
• Your program's output should only be the version number. No extraneous data.
• Your program may use whatever method you like to determine the version number. However, the output must follow rule 2; however you determine the version number, the output must only be the number.
• Your program only needs to output the major version of the language. For example, in FooBar 12.3.456789-beta, your program would only need to output 12.
• If your language puts words or symbols before or after the version number, you do not need to output those, and only the number. For example, in C89, your program only needs to print 89, and in C++0x, your program only needs to print 0.
• If you choose to print the full name or minor version numbers, e.g. C89 as opposed to C99, it must only print the name. C89 build 32 is valid, while error in C89 build 32: foo bar is not.
• Your program may not use a builtin, macro, or custom compiler flags to determine the language version.

# Scoring

Your score will be the code length divided by the number of versions it works in. Lowest score wins, good luck!

• What is a language version number? Who determines it? Aug 16, 2017 at 4:34
• I think that inverse-linear in the number of version does not welcome answers with high number of versions. Aug 16, 2017 at 5:23
• @user202729 I agree. Versatile Integer Printer did it well - score was (number of languages)^3 / (byte count).
– user45941
Aug 16, 2017 at 5:37
• What is the version for a language? Isn't we define a language as its interpreters / compilers here? Let we say, there is a version of gcc which has a bug that with certain C89 codes it produce an executable which behavior violate the C89 specification, and it was fixed on the next version of gcc. Should this count a valid solution if we write a piece of code base on this bug behavior to tell which gcc version is using? It targeting on different version of compiler, but NOT different version of language.
– tsh
Aug 16, 2017 at 7:21
• I don't get this. First you say "Your program's output should only be the version number.". Then you say "If you choose to print the full name or minor version numbers, e.g. C89 as opposed to C99, it must only print the name." So the first rule is not actually a requirement?
– pipe
Aug 16, 2017 at 9:13

# Ruby 1.x (<1.9) and 2.x, 10 8 bytes, score = 4

$><<?2%7  Try it: This works by exploiting the different behaviors of ?x between Ruby 1.x and 2.x. In Ruby 1.x, ?A (for example) returns 65 (the ASCII value of the character A), but in Ruby 2.0 it returns the one-character string "A". The code above is equivalent to this: val = ?2$> << val % 7


In Ruby 1.x (<1.9), the value of val is 50 (the ASCII value of the character 2), a Fixnum. Fixnum#% is the modulo operator, so 50 % 7 returns 1.

In Ruby 2.x, val is the string "2". String#% is an infix version of sprintf, so "2" % 7 is equivalent to sprintf("2", 7), where "2" is the format string. Since the format string doesn't contain any format sequences (e.g. %d), subsequent arguments are discarded and "2" is returned.

Finally, $> is an alias for $stdout, so $> << ... prints the result. • Ooh, nice! I was trying to do something like ?A==66?1:2 before I came across your answer Aug 8, 2018 at 1:39 # Python 2 and Python 3, 36 34 bytes, score 18 17 print(str(hash(float('-inf')))[1])  In Python 2, the hash of negative infinity is -271828 but in Python 3 it's -314159. Edit: Saved 2 bytes, 1 point of score, thanks to @ArBo. • squints Is this a deliberate e vs pi thing? – Jo King May 21, 2019 at 12:37 • @JoKing Yes; apparently when hash was first fixed to work on floating-point infinities the relevant developer used pi*1e5 and e*-1e5 as the hash values. At some point in Python 3 the has value for negative infinity got changed to be the negation of the hash value for infinity. – Neil May 21, 2019 at 12:48 # Python 3, Python 2, score 17.5 (35 bytes, 2 versions) try:exec("print 2") except:print(3)  ## Python 2, 35 bytes Try it online! ## Python 3, 35 bytes Try it online! Saved 5 bytes thanks to ETHproductions Not a good code golf answer, but a massive change! • Hmm, can you put each statement on the previous line? I.e try:exec("print 2")\nexcept:print(3) Aug 16, 2017 at 14:52 • @ETHproductions thanks! I didn't expect to win, thus I was a bit distracted. I mainly wanted to focus on the massive change between Python 2 and 3. Aug 16, 2017 at 14:59 ## PHP 4, 5, & 7: 59 bytes, score 19.67 <?list($a[],$a[])=array(5-strrpos('112','11'),7);echo$a[1];


It's way long but it does 3 versions!

list() assigns in backwards order in PHP 4 & 5, strrpos() only searches for a single character in PHP 4.

## C++11 and C++14, 96 bytes, score 48

#include<iostream>
#define M(x,...)__VA_ARGS__
int main(){std::cout<<(int[]){M(1'2,1'4,11)}[0];}


The code is based on the digit separator proposal which has very similar example code at the end.

online version

# Braingolf, 4 bytes, 2 versions, score = 2

0n6+


Try it online!

n (negate) was added in braingolf v0.7, meaning this will output 7 in v0.7 and newer, and 6 in v0.6 and older

In v0.7, n "negates" the 0 to a 1, then adds it to 6 to make 7, but in v0.6, the n does nothing, and so 0 is added to 6, resulting in 6. Both versions have implicit output.

This is technically the minor version, however there is only 1 major version of Braingolf, as Braingolf v1.0 isn't a thing yet, so this is about as major as the version numbers get.

# Idea of the solution

Each version hava added a new syntax features, so code vith them will cause a syntax error in smaller versions of the language.

• ES5: Octal constants 03 are disallowd in strict mode (unused in actual solutions)
• ES6: Octal constants 0o6 are introduced
• ES7: Pow operator is introduced: 7**1
• ES8: Trailing comma in function call is allowed: f(8,)
• ES9: Tagging of invalid strings is allowed: f\u9

So evaluating

eval('9;'+s)


on one of the examples will either return the the value, or throw a SyntaxError. But what value would it return? For all ES6-ES8 snippets the last calculated value will be the version. In ES9 snippet the 9 from 9; is return. So if we place a minimal supported version into catch block like

eval(try{eval('9;'+s)}catch(e){6})


we'll get the behaviour we want.

# Ecmascript 5+ (for 5, 6, 7, 8, 9); 124 / 5 = 24.8 points

alert(Math.max.apply(0,'0o6~7**1~f(8,)~f\\u9'.split('~').map(function f(s){return eval("try{eval('9;'+s)}catch(e){5}")})))

Tested in:

• IE8: Error (Object doesn't support property or method 'map')
• IE 11: 5
• Edge 14: 8
• Chrome 60: 8
• Firefox 55: 9

# Ecmascript 6+ (for 6, 7, 8, 9); 96 / 4 = 24 points

alert(Math.max(...'7**1~f(8,)~f\\u9'.split~.map(f=s=>eval(try{eval('9;'+s)}catch(e){6}))))

Tested in:

• Edge 14: 8
• Chrome 60: 8
• Firefox 55: 9
• Oh, now this is very nice :) I'm not sure about the inclusion of ES9, though, seeing as the official spec doesn't exist yet. I'd suggest including an explanation to help those unfamiliar with JS to understand what's going on here. Also, could you save any bytes using reduce instead of max? Oh, and this outputs 8 for both in Edge 14, if you want to include it. Aug 18, 2017 at 12:49
• @Shaggy, here is a single feature for ES 2018 which already got out of ES next and I used it. Aug 18, 2017 at 12:51
• Ah, I wasn't aware that that had made it out. Aug 18, 2017 at 13:02
• @Shaggy, added a description. Aug 18, 2017 at 13:04

# Julia 0.2-0.7, bytes = 59, versions = 6, score = 9.833

f()=[4,0,3,0,5,2,6,7][endof(subtypes(AbstractArray))-16]/10


Turns out each version of julia has had a different number of abstract array subtypes. So we use the number of subtypes to index into an array that contains the version number. The current nightly (0.7), currently has 25, but it is the fallback case anyway.

Julia 0.1 also has a different number of AbstractArray subtypes I would guess. However, julia 0.1 does not have the subtypes function. So I can't trivially retrieve them.

A significant improvement over my previous answer.

(Thanks @one-minute-more for almost halving the bytecount)

• Golfing this to 52 bytes, for a score of 8.7: [4,0,3,0,5,2,6,7][endof(subtypes(AbstractArray))-16]. Aug 18, 2017 at 17:08
• @one-more-minute thanks. I do need to add some boilerplate, but that is still quiet a saving. Aug 19, 2017 at 3:29

# Lua 5.3.3 / 5.2.4, 32 bytes / 2 = 16 points

print(54-#("a"):gsub("a?$","a"))  This actually abuses a bug in Lua 5.2's string.gsub function: In Lua 5.2, "a?$" would first match "a{endOfString}" and then "{endOfString}" a second time, which is obviously incorrect, because gsub may only match each character/anchor in the string once. This results in "a" being substituted into the string twice, giving a final string length of 2.

In Lua 5.3, the bug is fixed, so "a?$" only matches "a{endOfString}", thus matching the end of the string only once, which is the correct behavior. Therefore, "a" is substituted into the string just once, resulting in a string length of 1. The difference between the string length and 54 then gives the major/minor version number of the Lua interpreter, which is printed as follows: Lua 5.2.4: 52 Lua 5.3.3: 53 (I could only test on these two specific versions, but it should work on more) # Japt, 4 bytes / 2 versions = 2 J\+2  Alternative: 2\/2  # Wolfram Language (Mathematica), 30 bytes (5<= Score <=10) Floor[.8+Length@Names@"*"/615]  I tested this on Mathematica 10.2, which has 5683 named symbols and 11.3, which has 6280 names symbols. I used cloud.wolfram.com to test it on Mathematica 12.0 which has 6914 named symbols. With possibly some slight tweaking to the numbers, this could work for as many as 6 versions (see the graph at https://blog.wolfram.com/2018/06/21/weve-come-a-long-way-in-30-years-but-you-havent-seen-anything-yet/) which shows that the function count has grown pretty much linearly since v6.) Try it online! • 27 bytes – att May 22, 2019 at 23:56 ## Python 1, 2, and 3, 43 33 bytes, score 14.333 11 -10 bytes by using and and or instead of an if/else statement print(1and str(1<2)=="1"or 3/2*2)  Yes, there are already submissions for Python, but this one also differentiates Python 1 and 2. The difference between Python 3 and 2 are pretty well known, but with Python 1? It's very similar to Python 2. But I found something. In Python 1, a true statement evaluates to 1. In Python 2 and 3, it evaluates to True. By turning a true statement into a string, you can differentiate Python 1 and the later versions. • You can save 6 bytes by getting rid of the 1and and switching the condition to <"2". But you'll also need to add 5 bytes (unless there's a golfier way) to convert 3/2*2 to an int, since Python 3 computes it as a float 3.0, which isn't a valid output format for this challenge. Oct 12, 2021 at 17:37 # 05AB1E/05AB1E (legacy)/2sable, score 9 8⅔ (26 bytes with 3 versions) •äƵí•hR®di’ÿ (»Š)’ë’2sˆ¢’r  Explanation: Let's start with a bit of history of these three versions. The development of 05AB1E started at the start of 2016 (or actually, the very first git-commit was on December 21st, 2015). This new codegolf language was being built in Python as backend. Mid 2016 2sable was branched of that current 05AB1E version (July 7th, 2016 to be exact), and the strength of 2sable in comparison to that old 05AB1E version was added: implicit inputs. Later on implicit input was also added to 05AB1E, and 2sable became obsolete and basically a forgotten version right after it was created on that day July 7th, 2016. Then in mid-2018, a new 05AB1E version was being started, this time completely rewritten in Elixir instead of Python, with loads of new builtins added and some builtins changed or even removed. So, let's go over the code and see what it does in each of the three versions: •äƵí• # Legacy 05AB1E / new 05AB1E: push compressed integer 14793296 # 2sable: no-op (compressed integers didn't exist yet, # so nothing is pushed to the stack) h # Convert this from an integer to a hexadecimal string: "E1BA50" R # Reverse this string: "05AB1E" # (2sable: the stack remains empty for h and R) ® # Push -1 d # 2sable: check if -1 only consist of digits (falsey) # 05AB1E (legacy): check if -1 is an integer (truthy) # New 05AB1E: check if -1 is a non-negative (≥0) integer (falsey) i # If it is truthy: ’ÿ (»Š)’ # Push dictionary string "ÿ (legacy)", # where the ÿ is automatically filled with the top of the stack ë # Else: ’2sˆ¢’ # Push dictionary string "2sable" r # And reverse the stack # (05AB1E: stack changes from "05AB1E","2sable" to "2sable","05AB1E" # 2sable: stack stays the same "2sable") # (after which the top of the stack is output implicitly as result)  See this 05AB1E tip of mine (sections How to use the dictionary? and How to compress large integers?) to understand why ’ÿ (»Š)’ is "ÿ (legacy)", ’2sˆ¢’ is "2sable", and •äƵí• is 14793296. • Out of curiosity, is there a name for this sort of table/explanation method? Where the lines are ordered by English logic, but commands are padded to their place in the code. Nov 20, 2019 at 20:34 • @GezaKerecsenyi I to be honest have no idea. I kinda made it up for my answers tbh. I know some other people do their comments similarly, but no idea if it's a known convention and it has a name. Nov 20, 2019 at 21:29 • OK, thanks anyway! Nov 20, 2019 at 21:30 # VBA, 51 Bytes / 2 Codes = 25.5 Anonymous VBE immediate window function that outputs the VBA program version - works only in Excel 2011 and later. Outputs Version 7 for windows and Version 6 for Mac, as mac has yet to get version 7 of VBA Uses conditional compilation constant Mac to determine the VBA language version Sub a() o=7 #If Mac Then o=6 #End If Debug.?o End Sub  • @Shaggy, missed that, corrected Aug 16, 2017 at 17:02 # Lua 5.0, 5.1, 5.2, 5.3, score: 17.75 print("5."..(table.getn or load'return #...'){math.fmod,_ENV,math.ult})  math.fmod was math.mod before 5.1 _ENV was introduced in 5.2 for new environment system math.ult was introduced in 5.3 There was a problem: lua 5.0 don't know # for tables, but lua >= 5.2 don't have function table.getn, so I keep # isolated in string for lua 5.0, and loads in instead of table.getn for lua >= 5.2. ## K/Kona, 6 bytes, 2 versions - score 3 -4+@,0  Version 3 defaults to long numeric types, whereas 2 defaults to integer numerics. We enlist (,) to get a positively typed number (atoms are negative), we get its type (@), and we add that to -4. # RProgN 1/2, 4 bytes / 2 versions. Score = 2 1 1+  RProgN1 requires commands to be split into words, where as RProgN2 reads byte by byte by default. As such, RProgN1 can't do anything with 1+, so it just outputs 1, but RProgN2 can execute it, so it adds 1 to the 1, giving 2. Try RProgN1 Try RProgn2 # T-SQL, 41 bytes / 11 versions = 3.7 score SELECT MAX(cmptlevel)/10FROM sysdatabases  Note that this does not use a SQL "builtin, macro, or custom compiler flag"; that would be something like SELECT @@VERSION or SELECT SERVERPROPERTY('productversion'). Instead, this looks up the highest "compatibility level" for all databases on the server. Existing databases on a server might have older compatibility levels, but the system database tempdb is recreated each time the server is restarted, so will always have the compatibility level of the actual current version. (Using MAX is shorter than including WHERE name='tempdb'). I divide by 10 so it returns the actual SQL version number. I've successfully tested this on all versions between SQL 2005 and 2017, but I believe this will ultimately work in 11 distinct versions: Version Number Release Name 6 SQL Server 6.0 6.5 SQL Server 6.5 7 SQL Server 7.0 8 SQL Server 2000 9 SQL Server 2005 10 SQL Server 2008 (or SQL Server 2008 R2) 11 SQL Server 2012 12 SQL Server 2014 13 SQL Server 2016 14 SQL Server 2017 15 SQL Server 2019 (Pre-release)  Let me know if anyone has SQL 2000 or older still running to see if this works as expected. Note that my code uses a deprecated system table for widest compatibility, I'd normally use the newer sys.databases system view introduced in SQL 2005. # Whitespace, 43 bytes, 2 versions, score 21.5 Version 0.3 of Whitespace added the copy and slide instructions. Of those two, slide is exploited here. Slide removes the top n elements of the stack, keeping the top element. The code for version 0.3 slides 2 and version 0.2 ignores the slide and keeps the 2. SS T STSSSTT SSSTS SSSTSTTTS ST ST T SST ST  Written as more readable Whitespace assembly: push 0 # Push 0 to the stack printi # Print 0 as an integer push 3 # Push 3 to the stack push 2 # Push 2 to the stack push '.' # Push 46 ('.') to the stack slide 1 # Pop 2 if version 0.3 or skip instruction printc # Print 46 as character '.' printi # Print 2 or 3 as integer  # SmileBASIC (2 and 3), 4.5 bytes Thank you 12Me21 for helping out. ?3+CANCEL  In SB, variable declaration rules are lazy by default; if a variable name is not declared or assigned anywhere in scope, it is assumed to be 0. There's also a handful of reserved system variables and constants which varies between version and platform. In SB v2, CANCEL was a reserved variable that represented -1; this was intended for use with RESULT to check the state of a dialog box or other action. In SB v3, this variable is not present. Thus, when this program is run on a standard SB v2 environment it prints 2, and on a v3 environment it prints 3. # Whispers, 35 bytes / 3 versions = 11.66 >> R2 >> -1 > 3 > 2 > 1 >> Output 3  Try it online! (Whispers v1) Try it online! (Whispers v2) Unfortunately, Whispers v3 isn't currently on TIO, so you'll have to download it from the repo to try it. ## How they work This relies on the feature in Whispers which removes lines that contain (among other things) invalid instructions from the code before numbering the lines for execution. All three programs work by calling line 3 and outputting the number on that line. However, by removing lines that contain invalid instructions, we change which line line 3 actually is. As they all just output line 3's result, I'll focus the explanations on which line is line 3 and why. ### v1 In version 1, neither of the first two lines are valid instructions. Therefore, the code passes to the executor is > 3 > 2 > 1 >> Output 3  and line 3 simply returns $$\1\$$ ### v2 In version 2, the first line >> R2 is now a valid instruction: it takes a vector and outputs its polar form (i.e. magnitude and angle). However, the second line >> -1 is not yet valid, so the interpreter sees >> R2 > 3 > 2 > 1 >> Output 3  The first line shifts line 3 to refer to > 2, which just returns $$\2\$$. ### v3 The entire program is valid in the latest version Whispers, which includes a massive expansion of builtins, but the shortest new addition is the negate command: >> -1, which does not return $$\-1\$$, rather it takes in an argument and negates in. Therefore, the interpreter sees >> R2 >> -1 > 3 > 2 > 1 >> Output 3  Meaning that line 3 is now > 3, which, you guessed it, returns $$\3\$$. # Nim, 28 bytes / 2 versions = score 14 echo int compiles uint64.low  Try it online! (TIO only supports one version) In version 1.0, uint64 was made a proper ordinal type, so it's now possible to determine its lower bound. The program simply checks if the expression uint64.low compiles and converts the bool to an integer. # Python 2/3 REPL, 36 33 bytes, 36 33/2=38 16.5 score [3,2]['cmp' in dir(__builtins__)]  Try it in a Python shell. It (ab)uses removed builtins in Python 3. cmp was a builtin in Python 2, but was removed in Python 3, and it would result a truthy value in Python 2 and falsey in Python 3, and use it to index the [3,2] list, if it is 0, then it would output 3 and for 1 it would output 2. To make this solution, first I took the bulitins list from Python 2, made it a set, and subtracted set of Python 3 builtins from it s={'ArithmeticError', 'AssertionError', 'AttributeError', 'BaseException', 'BufferError', 'BytesWarning', 'DeprecationWarning', 'EOFError', 'Ellipsis', 'EnvironmentError', 'Exception', 'False', 'FloatingPointError', 'FutureWarning', 'GeneratorExit', 'IOError', 'ImportError', 'ImportWarning', 'IndentationError', 'IndexError', 'KeyError', 'KeyboardInterrupt', 'LookupError', 'MemoryError', 'NameError', 'None', 'NotImplemented', 'NotImplementedError', 'OSError', 'OverflowError', 'PendingDeprecationWarning', 'ReferenceError', 'RuntimeError', 'RuntimeWarning', 'StandardError', 'StopIteration', 'SyntaxError', 'SyntaxWarning', 'SystemError', 'SystemExit', 'TabError', 'True', 'TypeError', 'UnboundLocalError', 'UnicodeDecodeError', 'UnicodeEncodeError', 'UnicodeError', 'UnicodeTranslateError', 'UnicodeWarning', 'UserWarning', 'ValueError', 'Warning', 'ZeroDivisionError', '_', '__debug__', '__doc__', '__import__', '__name__', '__package__', 'abs', 'all', 'any', 'apply', 'basestring', 'bin', 'bool', 'buffer', 'bytearray', 'bytes', 'callable', 'chr', 'classmethod', 'cmp', 'coerce', 'compile', 'complex', 'copyright', 'credits', 'delattr', 'dict', 'dir', 'divmod', 'enumerate', 'eval', 'execfile', 'exit', 'file', 'filter', 'float', 'format', 'frozenset', 'getattr', 'globals', 'hasattr', 'hash', 'help', 'hex', 'id', 'input', 'int', 'intern', 'isinstance', 'issubclass', 'iter', 'len', 'license', 'list', 'locals', 'long', 'map', 'max', 'memoryview', 'min', 'next', 'object', 'oct', 'open', 'ord', 'pow', 'print', 'property', 'quit', 'range', 'raw_input', 'reduce', 'reload', 'repr', 'reversed', 'round', 'set', 'setattr', 'slice', 'sorted', 'staticmethod', 'str', 'sum', 'super', 'tuple', 'type', 'unichr', 'unicode', 'vars', 'xrange', 'zip'}-(set(dir(__builtins__)))  Which is {'reduce', 'cmp', 'basestring', 'reload', 'file', 'unicode', 'buffer', 'execfile', 'apply', 'raw_input', 'unichr', 'coerce', 'StandardError', 'xrange', 'intern', 'long'}  And for good sort from smallest to largest sorted(s,key=lambda x:len(x))  So we could use these builtins, amongst them, cmp is the shortest: 'cmp', 'file', 'long', 'apply', 'reduce', 'reload', 'buffer', 'unichr', 'coerce', 'xrange', 'intern', 'unicode', 'execfile', 'raw_input', 'basestring', 'StandardError'  • extra whitespace following 'cmp' Apr 12, 2021 at 19:41 • You can also save 3 bytes by subtracting from 3 instead of indexing into a list. Oct 12, 2021 at 17:27 ## PHP (42 chars, 4 versions) - score 10.5 echo max('$',0)?@(date(c)==c?4:7-"0x2"):8;


With the @ error control operator you shouldn't see the “Use of undefined constant” notice.

## PHP (59 chars, 4 versions)

This 15 chars per version attempt is based on fdiv, ereg and idate existence.
The line must be runnable through the codes from the years 2000 to 2022.

$a='is_callable';echo$a('fdiv')+6-2*$a('ereg')+$a('idate');


Both write a single character to stdout among 4,5,7 and 8, checked with 3v4l.org.

Interesting change between 7 and 8 :

echo max('$', 0) === max(0, '$') ? 8 : 7 ; // custom version detector


## PostScript, 36 bytes/3 versions = score of 12

00000000: 92aa 9238 283c 3c29 9260 7b28 7368 6669  ...8(<<).{(shfi
00000010: 6c6c 2992 607b 337d 7b32 7d92 557d 7b31  ll).{3}{2}.U}{1
00000020: 7d92 553d                                }.U=


Un-tokenized:

systemdict dup(<<)known{(shfill)known{3}{2}ifelse}{1}ifelse =


Tests for operators introduced in PostScript Levels 2 and 3.

<< was introduced in PS level 2. If this isn't present, it's PS level 1.

shfill was introduced in PS level 3. If this isn't present, it's PS level 2. (xpost currently prints 2, as shfill isn't implemented: Try it online!)

Ghostscript prints 3.

# Julia: 21 bytes, score: 10.5

(length([1:2])+V)/10   # where V takes value critical version minus 1


I cannot quite remember which version of julia this changed in (stuff gets deprecated very often), but there was some point in the language where this:

julia> [1:2]
2-element Array{Int64,1}:
1
2


became this:

julia> [1:2]
1-element Array{UnitRange{Int64},1}:
1:2


from one version* to the next. I think it was probably from v0.3 to v0.4? (in which case V = 2)

* julia is still in major version 0

## Mathematica 10/11 20 Bytes, Score=10

Since Echo was introduced in version 11

10+Echo@1/.Echo@1->0


Can extend to version 9 as well (since AbsArg was intro'd in V10) with

10-Last@AbsArg@1+Echo@1/.Echo@1:>0


but the score goes up to 34/3 or 11.33

# Scratch (Squeak 1.x, 2.0, and 3.0) (scratchblocks3 syntax), 92 bytes, score 30.666...

when gf clicked
if<<[cos v]of(90)>=(0
say[3
else
if<(l)=(1
say[1
else
say[2


Ungolfed, with explanation:

when gf clicked
if<<[cos v] of (90)> = (0) // cos 90 is 0 in Scratch 3, and some nonsense number in other versions
say [3]
else
add<not <> > to [list v] // "add <true> to a list" - this adds "1" to the list in 1.x, and the string "true" to the list in later versions
if<(list) = (1)> // all versions of Scratch automatically typecast everything to everything, including lists to strings, allowing for comparisons like this to be done seamlessly
say[1]
else
say[2]
end
end