19
\$\begingroup\$

Challenge

Peter has risen again to save us from the duplicate challenges!

Peter Taylor is dead, and there's no doubt about it (well, apart from the huge amount of doubt we have... but just ignore that). In his memory, you must write a program which determines whether a given user is alive or dead.

Further Information

A user is dead if they haven't been seen for more than a day, any less than that then they are alive. Check the last seen section found here:

Location of last seen

The input will be a user id (for example, mine is 30525, and Peter Taylor's is 194). Assume that all inputs are valid PPCG IDs.

If the user is alive, you should output:

[User name] is alive!

Where you replace [User name] for their username not their user id.

If the user is dead, you should output:

 Sadly, [User name] is dead.

T-SQL entrants using the SE Data Explorer are disallowed.

Winning

The shortest program in bytes wins.

Leaderboard

var QUESTION_ID=59763;OVERRIDE_USER=30525;function answersUrl(e){return"http://api.stackexchange.com/2.2/questions/"+QUESTION_ID+"/answers?page="+e+"&pagesize=100&order=desc&sort=creation&site=codegolf&filter="+ANSWER_FILTER}function commentUrl(e,s){return"http://api.stackexchange.com/2.2/answers/"+s.join(";")+"/comments?page="+e+"&pagesize=100&order=desc&sort=creation&site=codegolf&filter="+COMMENT_FILTER}function getAnswers(){jQuery.ajax({url:answersUrl(answer_page++),method:"get",dataType:"jsonp",crossDomain:!0,success:function(e){answers.push.apply(answers,e.items),answers_hash=[],answer_ids=[],e.items.forEach(function(e){e.comments=[];var s=+e.share_link.match(/\d+/);answer_ids.push(s),answers_hash[s]=e}),e.has_more||(more_answers=!1),comment_page=1,getComments()}})}function getComments(){jQuery.ajax({url:commentUrl(comment_page++,answer_ids),method:"get",dataType:"jsonp",crossDomain:!0,success:function(e){e.items.forEach(function(e){e.owner.user_id===OVERRIDE_USER&&answers_hash[e.post_id].comments.push(e)}),e.has_more?getComments():more_answers?getAnswers():process()}})}function getAuthorName(e){return e.owner.display_name}function process(){var e=[];answers.forEach(function(s){var r=s.body;s.comments.forEach(function(e){OVERRIDE_REG.test(e.body)&&(r="<h1>"+e.body.replace(OVERRIDE_REG,"")+"</h1>")});var a=r.match(SCORE_REG);a&&e.push({user:getAuthorName(s),size:+a[2],language:a[1],link:s.share_link})}),e.sort(function(e,s){var r=e.size,a=s.size;return r-a});var s={},r=1,a=null,n=1;e.forEach(function(e){e.size!=a&&(n=r),a=e.size,++r;var t=jQuery("#answer-template").html();t=t.replace("{{PLACE}}",n+".").replace("{{NAME}}",e.user).replace("{{LANGUAGE}}",e.language).replace("{{SIZE}}",e.size).replace("{{LINK}}",e.link),t=jQuery(t),jQuery("#answers").append(t);var o=e.language;/<a/.test(o)&&(o=jQuery(o).text()),s[o]=s[o]||{lang:e.language,user:e.user,size:e.size,link:e.link}});var t=[];for(var o in s)s.hasOwnProperty(o)&&t.push(s[o]);t.sort(function(e,s){return e.lang>s.lang?1:e.lang<s.lang?-1:0});for(var c=0;c<t.length;++c){var i=jQuery("#language-template").html(),o=t[c];i=i.replace("{{LANGUAGE}}",o.lang).replace("{{NAME}}",o.user).replace("{{SIZE}}",o.size).replace("{{LINK}}",o.link),i=jQuery(i),jQuery("#languages").append(i)}}var ANSWER_FILTER="!t)IWYnsLAZle2tQ3KqrVveCRJfxcRLe",COMMENT_FILTER="!)Q2B_A2kjfAiU78X(md6BoYk",answers=[],answers_hash,answer_ids,answer_page=1,more_answers=!0,comment_page;getAnswers();var SCORE_REG=/<h\d>\s*([^\n,]*[^\s,]),.*?(\d+)(?=[^\n\d<>]*(?:<(?:s>[^\n<>]*<\/s>|[^\n<>]+>)[^\n\d<>]*)*<\/h\d>)/,OVERRIDE_REG=/^Override\s*header:\s*/i;
body{text-align:left!important}#answer-list,#language-list{padding:10px;width:290px;float:left}table thead{font-weight:700}table td{padding:5px}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script> <link rel="stylesheet" type="text/css" href="//cdn.sstatic.net/codegolf/all.css?v=83c949450c8b"> <div id="answer-list"> <h2>Leaderboard</h2> <table class="answer-list"> <thead> <tr><td></td><td>Author</td><td>Language</td><td>Size</td></tr></thead> <tbody id="answers"> </tbody> </table> </div><div id="language-list"> <h2>Winners by Language</h2> <table class="language-list"> <thead> <tr><td>Language</td><td>User</td><td>Score</td></tr></thead> <tbody id="languages"> </tbody> </table> </div><table style="display: none"> <tbody id="answer-template"> <tr><td>{{PLACE}}</td><td>{{NAME}}</td><td>{{LANGUAGE}}</td><td>{{SIZE}}</td><td><a href="{{LINK}}">Link</a></td></tr></tbody> </table> <table style="display: none"> <tbody id="language-template"> <tr><td>{{LANGUAGE}}</td><td>{{NAME}}</td><td>{{SIZE}}</td><td><a href="{{LINK}}">Link</a></td></tr></tbody> </table>

\$\endgroup\$
  • 2
    \$\begingroup\$ Why disallow T-SQL/SEDE? \$\endgroup\$ – Geobits Oct 5 '15 at 17:42
  • 1
    \$\begingroup\$ Needs a description of where to find the number for "last seen". \$\endgroup\$ – feersum Oct 5 '15 at 17:42
  • \$\begingroup\$ @feersum It is the title tag of the last seen value. \$\endgroup\$ – mınxomaτ Oct 5 '15 at 17:46
  • \$\begingroup\$ @feersum Good enough? \$\endgroup\$ – Beta Decay Oct 5 '15 at 17:46
  • 1
    \$\begingroup\$ I think parsing is more relevant to this than internet. \$\endgroup\$ – mınxomaτ Oct 5 '15 at 20:41
4
\$\begingroup\$

CJam, 115 bytes

"codegolf.xyz/u/"r+g_N/{"s=\"r"/_0='>&!*1>s_"da"#)\_el=!|}#)"Sadly, %s is dead.""%s is alive!"?\"\"User "/1='"/1<e%

The idea is the same as in my Bash answer, except that this answer doesn't use regular expressions, because CJam doesn't have regular expressions...

The online interpreter doesn't perform web requests, so this will only work from the command line.

\$\endgroup\$
8
\$\begingroup\$

Bash, 151 bytes

r="curl -L codegolf.xyz/u/$1";u=`$r|grep -Pom1 '(?<="User )[^"]*'`
$r|grep -Pq '^[^>]*s="r.*(da|[A-Z])'&&echo "Sadly, $u is dead."||echo "$u is alive!"

As usual, output to STDERR is ignored.

Example run

$ ./is-dead 30525 2>&-
Beta Decay is alive!
$ ./is-dead 44935 2>&-
Sadly, Alpha Decay is dead.

Idea

This code greps for lines containing s="r that do not have > before its occurrence. For example:

Last seen <span title="2015-09-17 12:00:00Z" class="relativetime">just now</span>
  • If the match is followed by the string da, it contains the word yesterday or days (as in 2 days ago).
  • If the match is followed by an uppercase letter, it contains the name of a month.
  • In all other cases, the user is undead alive.

The user's name is extracted from a Twitter meta tag. Example:

<meta name="twitter:title" property="og:title" itemprop="title name" content="User Dennis">
\$\endgroup\$
  • 3
    \$\begingroup\$ I've updated my profile, and suddenly I'm dead. :( \$\endgroup\$ – TessellatingHeckler Oct 6 '15 at 1:24
  • 5
    \$\begingroup\$ You are no longer dead to me. \$\endgroup\$ – Dennis Oct 6 '15 at 3:04
  • 1
    \$\begingroup\$ That's a relief! \$\endgroup\$ – TessellatingHeckler Oct 6 '15 at 3:26
  • \$\begingroup\$ Who registered that domain? \$\endgroup\$ – jimmy23013 Oct 6 '15 at 5:38
  • \$\begingroup\$ @jimmy23013 minxomat \$\endgroup\$ – Dennis Oct 6 '15 at 5:38
7
\$\begingroup\$

Javascript ES6, 234 bytes

document.write(`<script src="//api.stackexchange.com/users/${prompt(a=d=>{n=(x=d.items[0]).display_name,alert((Date.now()/1e3)-x.last_access_date>86400?`Sadly, ${n} is dead.`:n+' is alive!')})}?site=codegolf&callback=a">\x3C/script>`)

Annotated version

// Inserts a script tag to perform a JSONP callback request on the stackexchange API
document.write(`
  <script src="//api.stackexchange.com/users/${
    prompt( // interpolate user input into url
      a = d =>{ // declare a in global scope
        n = (x = d.items[0]).display_name, // alias the user object and name
        alert(
          (Date.now() / 1e3) - x.last_access_date > 86400 
          ? `Sadly, ${n} is dead.` // a day or less since last seen
          : n + ' is alive!' // more than a day since last seen
        )
      }
    )
  }?site=codegolf&callback=a">\x3C/script>` // escaping that prevents early termination of enclosing script tag
)
\$\endgroup\$
  • \$\begingroup\$ Fails to parse apostrophe in my name, yet correctly parses every other character. XD +1 for in-browser testing \$\endgroup\$ – Conor O'Brien Oct 6 '15 at 1:30
  • 1
    \$\begingroup\$ @CᴏɴᴏʀO'Bʀɪᴇɴ Damn the API entity encodes it. \$\endgroup\$ – George Reith Oct 6 '15 at 1:35
7
\$\begingroup\$

PowerShell (v4), 228 217 209, 157 bytes

$u=($x=curl "codegolf.xyz/u/$args").BaseResponse.ResponseURI.Segments[-1]
if($x-match'n <(.*?)((c|n|ur)s* ago|w)<'){"$u is alive!"}else{"Sadly, $u is dead."}

e.g.

PS C:\> test.ps1 30525
beta-decay is alive!

C:\> test.ps1 67
Sadly, keith-randall is dead.


#Previous 209 byte version:
$f={$u=((curl "api.stackexchange.com/2.2/users/$($args)?&site=codegolf"
)|ConvertFrom-Json).Items;$d=$u.display_name;if((get-date -U %s)-
$u.last_access_date-gt86400){"Sadly, $d is dead."}else{"$d is alive!"}}

(Sorry, Keith Randall, you were just the first account I found with >1 day since last seen date).

I was happy with the solid, reliable 209 byte one calling the API, but screen-scraping is the way to go for golf.

  • This now pulls the username from the redirected URI - but it is a name rather than a numeric ID, as required.

  • And it matches the line Last seen <span title="2015-10-03 13:15:38Z" class="relativetime">2 days ago</span> with n <... [nr]s ago< trying to catch sec(s) ago, min(s) ago, hour(s) ago, and just now, and miss "days weeks, months" ago, or the long term dates+times. And trying to catch 'last seen' and not the other relativetimes. (Thanks Dennis).

NB. curl is a default alias for Invoke-WebRequest, it's not the standard curl program ported to Windows.

\$\endgroup\$
  • 2
    \$\begingroup\$ There's also secs ago, 1 min ago (no s), and I think I've seen just now. \$\endgroup\$ – Dennis Oct 6 '15 at 6:00
  • \$\begingroup\$ @Dennis I think that's fixed now, thanks. (at the cost of falling behind your Bash answer; maybe I should try shamelessly using your pattern matches...). \$\endgroup\$ – TessellatingHeckler Oct 6 '15 at 6:18
5
\$\begingroup\$

R, 384 350 bytes

This one's for you, Peter!

u=scan();D=as.POSIXlt;J=jsonlite::fromJSON(gsub("/\\*\\*/a|[()]|;$","",httr::content(httr::GET(paste0("http://api.stackexchange.com/2.2/users/",u,"?site=codegolf&callback=a")),,"text")))$items;l=D(J$last_access_date,z<-"UTC","1970-01-01");n=D(Sys.time(),z);U=J$display_name;if(as.Date(n)-as.Date(l)>1)cat("Sadly,",U,"is dead.")else cat(U,"is alive!")

Note that this requires the httr and jsonlite packages to be installed, though they don't have to be imported for this code to work since we're referencing namespaces explicitly.

Ungolfed:

# Read a user ID from STDIN
u <- scan()

# Create a request object using the SE API v2.2
request <- httr::GET(paste0("http://api.stackexchange.com/2.2/users/", u,
                            "?site=codegolf&callback=a"))

# Read the contents of the request into a ill-formed JSON string
body <- httr::content(request, type = "text")

# Parse out a valid string and get the associated fields
J <- jsonlite::fromJSON(gsub("/\\*\\*/a|[()]|;$", "", body))$items

# Get the last accessed date as a POSIX datetime object
l <- as.POSIXlt(J$last_access_date, "UTC", "1970-01-01")

# Get the current date
n <- as.POSIXlt(Sys.time(), "UTC")

# Extract the username
U <- J$display_name

# Determine whether the user has died
if (as.Date(n) - as.Date(l) > 1) {
    cat("Sadly," U, "is dead.")
} else {
    cat(U, "is alive!")
}

Saved 5 bytes on my previous approach and corrected an error in my current approach thanks to minxomat!

\$\endgroup\$
  • \$\begingroup\$ I dunno about R, but can't u save some bytes by putting the string literal "/users/" into a variable? \$\endgroup\$ – mınxomaτ Oct 5 '15 at 19:40
  • \$\begingroup\$ @minxomat Sure can, thanks! \$\endgroup\$ – Alex A. Oct 5 '15 at 19:44
  • \$\begingroup\$ \n and ; take the same amount of bytes. So your code doesn't have to look like that \$\endgroup\$ – OganM Oct 5 '15 at 22:36
  • \$\begingroup\$ @OganM Actually, in R, scan() will think succeeding lines are the stuff you want to enter from STDIN (who knows why), so the semicolons are required in this case. But in general you're right. \$\endgroup\$ – Alex A. Oct 6 '15 at 0:09
5
\$\begingroup\$

AutoIt, 320 316 308 bytes

#include<String.au3>
#include<Date.au3>
$0=_StringBetween
$1=BinaryToString(InetRead('http://codegolf.xyz/u/'&ClipGet()))
$2=_DateDiff('D',StringReplace($0($1,'Last seen <span title="',' ')[0],'-','/'),@YEAR&'/'&@MON&'/'&@MDAY)
ConsoleWrite(($2?'Sadly, ':'')&$0($1,'r ','- P')[0]&'is '&($2?'dead.':'alive!'))

_DateDiff calcs the difference in days ('D'). It will be 0 if the difference is less than 1 day, so we can use it as a boolean value. The title tag of the "last seen" value contains a (almost) standard timestamp.

\$\endgroup\$
  • 2
    \$\begingroup\$ I haven't seen auto it in over a decade. Lord, take my upvote. \$\endgroup\$ – Qix Oct 6 '15 at 4:34
1
\$\begingroup\$

PHP, 187 bytes

Fairly simplistic approach, using the codegolf.xyz domain, only slightly different item here is that I attempt to get both variables at once. Tested on a few users with correct results, please let me know if there are some problem areas though!

<?preg_match('/"User ([^"]+)".+?"([^"]+)" class="r/s',file_get_contents("http://codegolf.xyz/u/$argv[1]"),$m);echo time()-strtotime($m[2])<86400?"$m[1] is alive!":"Sadly, $m[1] is dead.";

Usage:

php 59763.php 30525
\$\endgroup\$
  • \$\begingroup\$ $m[1] is appears two times. Wouldn't it be cheaper to print this first and use the ternary just for the dead/alive part? \$\endgroup\$ – mınxomaτ Oct 6 '15 at 18:33
  • \$\begingroup\$ @minxomat I did play with that, but the Sadly, ... part got in the way... Can't seem to get my head around it! \$\endgroup\$ – Dom Hastings Oct 6 '15 at 18:44
  • \$\begingroup\$ Echo the Sadly first, then assign the result from time()-strtotime($m[2]) to a single-char variable. Then echo $m[1] is and lastly the dead/alive. That's how I did it - dunno if this will work in PHP :) \$\endgroup\$ – mınxomaτ Oct 6 '15 at 18:49
  • \$\begingroup\$ @minxomat Ahhh, so simple, But I couldn't get my head around it! Thanks! I'll update as soon as I can test again! \$\endgroup\$ – Dom Hastings Oct 6 '15 at 18:52
1
\$\begingroup\$

Groovy, 355 bytes

import groovy.json.JsonSlurper;import java.util.zip.GZIPInputStream;def d = new JsonSlurper().parseText(new GZIPInputStream(new URL("http://api.stackexchange.com/2.2/users/${args[0]}?site=codegolf").newInputStream()).getText()).items[0];def n = d.display_name;println d.last_access_date*1000l<new Date().time-8.64E7?"Sadly, ${n} is dead.":"${n} is alive!"

Uncompressed source

import groovy.json.JsonSlurper;
import java.util.zip.GZIPInputStream;

def rawText = new GZIPInputStream(new URL("http://api.stackexchange.com/2.2/users/${args[0]}?site=codegolf").newInputStream()).getText()
def json = new JsonSlurper().parseText(rawText).items[0]
def name = json.display_name
//We have to correct for java date returning in millis
def lastAccess = json.last_access_date * 1000l
def yesterday = new Date().time - 86400000
if (lastAccess < yesterday) {
    println "Sadly, ${name} is dead."
} else {
    println "${name} is alive!"
}
\$\endgroup\$
  • \$\begingroup\$ You can save 42 bytes (brings it down to 313 bytes) if you just do new groovy.json.JsonSlurper and new java.util.zip.GZIPInputStream instead of the imports. Importing only typically pays off if you use a class a lot. \$\endgroup\$ – a spaghetto Oct 11 '15 at 21:07

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.