21
\$\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\$
7
  • 2
    \$\begingroup\$ Why disallow T-SQL/SEDE? \$\endgroup\$
    – Geobits
    Oct 5, 2015 at 17:42
  • 1
    \$\begingroup\$ Needs a description of where to find the number for "last seen". \$\endgroup\$
    – feersum
    Oct 5, 2015 at 17:42
  • \$\begingroup\$ @feersum It is the title tag of the last seen value. \$\endgroup\$
    – user42643
    Oct 5, 2015 at 17:46
  • \$\begingroup\$ @feersum Good enough? \$\endgroup\$
    – Beta Decay
    Oct 5, 2015 at 17:46
  • 1
    \$\begingroup\$ I think parsing is more relevant to this than internet. \$\endgroup\$
    – user42643
    Oct 5, 2015 at 20:41

8 Answers 8

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\$
5
  • 3
    \$\begingroup\$ I've updated my profile, and suddenly I'm dead. :( \$\endgroup\$ Oct 6, 2015 at 1:24
  • 5
    \$\begingroup\$ You are no longer dead to me. \$\endgroup\$
    – Dennis
    Oct 6, 2015 at 3:04
  • 1
    \$\begingroup\$ That's a relief! \$\endgroup\$ Oct 6, 2015 at 3:26
  • \$\begingroup\$ Who registered that domain? \$\endgroup\$
    – jimmy23013
    Oct 6, 2015 at 5:38
  • \$\begingroup\$ @jimmy23013 minxomat \$\endgroup\$
    – Dennis
    Oct 6, 2015 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\$
2
  • \$\begingroup\$ Fails to parse apostrophe in my name, yet correctly parses every other character. XD +1 for in-browser testing \$\endgroup\$ Oct 6, 2015 at 1:30
  • 1
    \$\begingroup\$ @CᴏɴᴏʀO'Bʀɪᴇɴ Damn the API entity encodes it. \$\endgroup\$ Oct 6, 2015 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
  • 2
    \$\begingroup\$ There's also secs ago, 1 min ago (no s), and I think I've seen just now. \$\endgroup\$
    – Dennis
    Oct 6, 2015 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\$ Oct 6, 2015 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\$
4
  • \$\begingroup\$ I dunno about R, but can't u save some bytes by putting the string literal "/users/" into a variable? \$\endgroup\$
    – user42643
    Oct 5, 2015 at 19:40
  • \$\begingroup\$ @minxomat Sure can, thanks! \$\endgroup\$
    – Alex A.
    Oct 5, 2015 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, 2015 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, 2015 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\$
1
  • 2
    \$\begingroup\$ I haven't seen auto it in over a decade. Lord, take my upvote. \$\endgroup\$ Oct 6, 2015 at 4:34
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\$
2
\$\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\$
1
  • \$\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\$ Oct 11, 2015 at 21:07
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\$
4
  • \$\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\$
    – user42643
    Oct 6, 2015 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\$ Oct 6, 2015 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\$
    – user42643
    Oct 6, 2015 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\$ Oct 6, 2015 at 18:52

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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