text:
Minimal Word Search
Last week, we worked to create the shortest 1-D string using the top 10,000 words in the English language.  Now, lets try the same challenge in 2D!
What you need to do is to take all of the above words, and put them in a rectangle as small as possible, allowing for overlaps.  For example, if your words were ["ape","pen","ab","be","pa"], then a possible rectangle would be:
.b..
apen

The above rectangle would give a score of 5.
Rules:

Overlapping multiple letters in a word is allowed
Words can go in any of the 8 directions
Words cannot wrap around
You can use any character for the empty locations

You need to create a word search that contains these top 10,000 words in English (according to Google).  Your score is equal to the number of characters in your word search (excluding unused characters).  If there is a tie, or if a submission is proven to be optimal, then the submission that is first posted wins.

I'd like to note that I am aware of this previous word search challenge, but given that none of the answers there will run in a reasonable amount of time for this challenge, I don't believe its a duplicate.

Related.

I fear the optimal solution will turn out to be an n x 1 grid, making this problem ultimately the same as the last one (reasoning: tangent intersections will rarely save many characters but will often introduce "holes", wasting space). Maybe you should score it on the width + height, rather than width * height, so that it strongly favours square solutions (more interesting).

Hmmm... I fear that solutions will simply be word strings stacked on top of each other, then.  I think not scoring empty locations might be a good idea

Risk with that is there's no need to keep the grid size small; a 1000x1000 grid with a sprawling horizontal and vertical list would score the same as a tightened spiral pattern / similar. Maybe try width+height, then letters-excluding-blanks as a tie-breaker? Might need a bit more thought. Edit: or maybe letters-excluding-blanks first then width+height as a tie-breaker would work better.

I'm OK with a sprawling grid. The best solutions will still try to maximize overlap

Rust, 31430 30081 used characters
This is a greedy algorithm of sorts: we start with an empty grid, and repeatedly add the word that can be added with the fewest new letters, with ties broken by preferring longer words.  To make this run quickly, we maintain a priority queue of candidate word placements (implemented as a vector of vectors of deques, with a vector for each number of new letters, containing a deque for each word length).  For each newly added letter, we enqueue all candidate placements that run through that letter.
Compile and run with rustc -O wordsearch.rs; ./wordsearch < google-10000-english.txt.  On my laptop, this runs in 70 seconds, using 531 MiB RAM.
The output fits in a rectangle with 248 columns and 253 rows.

Code
use std::collections::{HashMap, HashSet, VecDeque};
use std::io::prelude::*;
use std::iter::once;
use std::vec::Vec;

type Coord = i16;
type Pos = (Coord, Coord);
type Dir = u8;
type Word = u16;

struct Placement { word: Word, dir: Dir, pos: Pos }

static DIRS: [Pos; 8] =
    [(1, 0), (1, 1), (0, 1), (-1, 1), (-1, 0), (-1, -1), (0, -1), (1, -1)];

fn fit(grid: &HashMap<Pos, u8>, (x, y): Pos, d: Dir, word: &String) -> Option<usize> {
    let (dx, dy) = DIRS[d as usize];
    let mut n = 0;
    for (i, c) in word.bytes().enumerate() {
        if let Some(c1) = grid.get(&(x + (i as Coord)*dx, y + (i as Coord)*dy)) {
            if c != *c1 {
                return None;
            }
        } else {
            n += 1;
        }
    }
    return Some(n)
}

struct PlacementQueue { queue: Vec<Vec<VecDeque<Placement>>>, extra: usize }

impl PlacementQueue {
    fn new() -> PlacementQueue {
        return PlacementQueue { queue: Vec::new(), extra: std::usize::MAX }
    }

    fn enqueue(self: &mut PlacementQueue, extra: usize, total: usize, placement: Placement) {
        while self.queue.len() <= extra {
            self.queue.push(Vec::new());
        }
        while self.queue[extra].len() <= total {
            self.queue[extra].push(VecDeque::new());
        }
        self.queue[extra][total].push_back(placement);
        if self.extra > extra {
            self.extra = extra;
        }
    }

    fn dequeue(self: &mut PlacementQueue) -> Option<Placement> {
        while self.extra < self.queue.len() {
            let mut subqueue = &mut self.queue[self.extra];
            while !subqueue.is_empty() {
                let total = subqueue.len() - 1;
                if let Some(placement) = subqueue[total].pop_front() {
                    return Some(placement);
                }
                subqueue.pop();
            }
            self.extra += 1;
        }
        return None
    }
}

fn main() {
    let stdin = std::io::stdin();
    let all_words: Vec<String> =
        stdin.lock().lines().map(|l| l.unwrap()).collect();
    let words: Vec<&String> = {
        let subwords: HashSet<&str> =
            all_words.iter().flat_map(|word| {
                (0..word.len() - 1).flat_map(move |i| {
                    (i + 1..word.len() - (i == 0) as usize).map(move |j| {
                        &word[i..j]
                    })
                })
            }).collect();
        all_words.iter().filter(|word| !subwords.contains(&word[..])).collect()
    };
    let letters: Vec<Vec<(usize, usize)>> =
        (0..128).map(|c| {
            words.iter().enumerate().flat_map(|(w, word)| {
                word.bytes().enumerate().filter(|&(_, c1)| c == c1).map(move |(i, _)| (w, i))
            }).collect()
        }).collect();

    let mut used = vec![false; words.len()];
    let mut remaining = words.len();
    let mut grids: Vec<HashMap<Pos, u8>> = Vec::new();

    while remaining != 0 {
        let mut grid: HashMap<Pos, u8> = HashMap::new();
        let mut queue = PlacementQueue::new();
        for (w, word) in words.iter().enumerate() {
            if used[w] {
                continue;
            }
            queue.enqueue(0, word.len(), Placement {
                pos: (0, 0),
                dir: 0,
                word: w as Word
            });
        }

        while let Some(placement) = queue.dequeue() {
            if used[placement.word as usize] {
                continue;
            }
            let word = words[placement.word as usize];
            if let None = fit(&grid, placement.pos, placement.dir, word) {
                continue;
            }
            let (x, y) = placement.pos;
            let (dx, dy) = DIRS[placement.dir as usize];
            let new_letters: Vec<(usize, u8)> = word.bytes().enumerate().filter(|&(i, _)| {
                !grid.contains_key(&(x + (i as Coord)*dx, y + (i as Coord)*dy))
            }).collect();
            for (i, c) in word.bytes().enumerate() {
                grid.insert((x + (i as Coord)*dx, y + (i as Coord)*dy), c);
            }
            used[placement.word as usize] = true;
            remaining -= 1;

            for (i, c) in new_letters {
                for &(w1, j) in &letters[c as usize] {
                    if used[w1] {
                        continue;
                    }
                    let word1 = words[w1];
                    for (d1, &(dx1, dy1)) in DIRS.iter().enumerate() {
                        let pos1 = (
                            x + (i as Coord)*dx - (j as Coord)*dx1,
                            y + (i as Coord) - (j as Coord)*dy1);
                        if let Some(extra1) = fit(&grid, pos1, d1 as Dir, word1) {
                            queue.enqueue(extra1, word1.len(), Placement {
                                pos: pos1,
                                dir: d1 as Dir,
                                word: w1 as Word
                            });
                        }
                    }
                }
            }
        }
        grids.push(grid);
    }

    let width = grids.iter().map(|grid| {
        grid.iter().map(|(&(x, _), _)| x).max().unwrap() -
            grid.iter().map(|(&(x, _), _)| x).min().unwrap() + 1
    }).max().unwrap();
    print!(
        "{}",
        grids.iter().flat_map(|grid| {
            let x0 = grid.iter().map(|(&(x, _), _)| x).min().unwrap();
            let y0 = grid.iter().map(|(&(_, y), _)| y).min().unwrap();
            let y1 = grid.iter().map(|(&(_, y), _)| y).max().unwrap();
            (y0..y1 + 1).flat_map(move |y| {
                (x0..x0 + width).map(move |x| {
                    *grid.get(&(x, y)).unwrap_or(&('.' as u8)) as char
                }).chain(once('\n').take(1))
            })
        }).collect::<String>()
    );
}

I haven't read the code yet, but do you do anything to encourage non-linear placements? I would have expected an algorithm like this to end up with a handful of crossing super-strings, but it looks like you're getting some pretty good space-filling.

@Dave Nothing specific, it just works out that way. The super-strings never get so long that better non-linear placements can never be found, probably because there are so many more non-linear placements to choose from.

starts with "congratulations", ends with "extraordinary"

I didn´t catch that you can go diagonal too. thanks for the picture. I don´t know if I should wish for comments on the code blocks. :)

C++, 27243 character grid (248x219, 50.2% filled)
(Posting this as a new answer because I'd like to keep the 1D bound I originally posted as a reference)
This blatantly rips off is heavily inspired by @AndersKaseorg's answer in its main structure, but has a couple of tweaks. First, I use my original program to merge strings until the best available overlap is just 3 characters. Then I use the method AndersKaseorg describes to progressively fill a 2D grid using these generated strings. The constraints are a little different too: it still tries to add the fewest characters each time, but ties are broken by favouring square grids first, then small grids, and finally by adding the longest word.
The behaviour it displays is to alternate between periods of filling in space and rapidly expanding the grid (unfortunately it ran out of words just after a rapid expansion stage, so there's a lot of blank space around the edges). I suspect with some tweaking of the cost function it could be made to get better than 50% space filling.
There are 2 executables here (to avoid the need to re-run the whole process when iteratively improving the algorithm). The output of one can be piped directly into the other:
#include <iostream>
#include <string>
#include <vector>
#include <algorithm>
#include <cstdlib>

std::size_t calcOverlap(const std::string &a, const std::string &b, std::size_t limit, std::size_t minimal) {
    std::size_t la = a.size();
    for(std::size_t p = std::min(std::min(la, b.size()), limit + 1); -- p > minimal; ) {
        if(a.compare(la - p, p, b, 0, p) == 0) {
            return p;
        }
    }
    return 0;
}

bool isSameReversed(const std::string &a, const std::string &b) {
    std::size_t l = a.size();
    if(b.size() != l) {
        return false;
    }
    for(std::size_t i = 0; i < l; ++ i) {
        if(a[i] != b[l-i-1]) {
            return false;
        }
    }
    return true;
}

int main(int argc, const char *const *argv) {
    // Usage: prog [<stop_threshold>]

    std::size_t stopThreshold = 3;

    if(argc >= 2) {
        char *check;
        long v = std::strtol(argv[1], &check, 10);
        if(check == argv[1] || v < 0) {
            std::cerr
                << "Invalid stop threshold. Should be an integer >= 0"
                << std::endl;
            return 1;
        }
        stopThreshold = v;
    }

    std::vector<std::string> words;

    // Load all words from input and their reverses (words can be backwards now)
    while(true) {
        std::string word;
        std::getline(std::cin, word);
        if(word.empty()) {
            break;
        }
        words.push_back(word);
        std::reverse(word.begin(), word.end());
        words.push_back(std::move(word));
    }

    std::cerr
        << "Input word count: " << words.size() << std::endl;

    // Remove all fully subsumed words

    for(auto p = words.begin(); p != words.end(); ) {
        bool subsumed = false;
        for(auto i = words.begin(); i != words.end(); ++ i) {
            if(i == p) {
                continue;
            }
            if(i->find(*p) != std::string::npos) {
                subsumed = true;
                break;
            }
        }
        if(subsumed) {
            p = words.erase(p);
        } else {
            ++ p;
        }
    }

    std::cerr
        << "After subsuming checks: " << words.size()
        << std::endl;

    // Sort words longest-to-shortest (not necessary but doesn't hurt. Makes finding maxlen a tiny bit easier)
    std::sort(words.begin(), words.end(), [](const std::string &a, const std::string &b) {
        return a.size() > b.size();
    });

    std::size_t maxlen = words.front().size();

    // Repeatedly combine most-compatible words until we reach the threshold
    std::size_t bestPossible = maxlen - 1;
    while(words.size() > 2) {
        auto bestA = words.begin();
        auto bestB = -- words.end();
        std::size_t bestOverlap = 0;
        for(auto p = ++ words.begin(), e = words.end(); p != e; ++ p) {
            if(p->size() - 1 <= bestOverlap) {
                continue;
            }
            for(auto q = words.begin(); q != p; ++ q) {
                std::size_t overlap = calcOverlap(*p, *q, bestPossible, bestOverlap);
                if(overlap > bestOverlap && !isSameReversed(*p, *q)) {
                    bestA = p;
                    bestB = q;
                    bestOverlap = overlap;
                }
                overlap = calcOverlap(*q, *p, bestPossible, bestOverlap);
                if(overlap > bestOverlap && !isSameReversed(*p, *q)) {
                    bestA = q;
                    bestB = p;
                    bestOverlap = overlap;
                }
            }
            if(bestOverlap == bestPossible) {
                break;
            }
        }
        if(bestOverlap <= stopThreshold) {
            break;
        }
        std::string newStr = std::move(*bestA);
        newStr.append(*bestB, bestOverlap, std::string::npos);

        if(bestA == -- words.end()) {
            words.pop_back();
            *bestB = std::move(words.back());
            words.pop_back();
        } else {
            *bestB = std::move(words.back());
            words.pop_back();
            *bestA = std::move(words.back());
            words.pop_back();
        }

        // Remove any words which are now in the result (forward or reverse)
        // (would not be necessary if we didn't have the reversed forms too)
        std::string newRev = newStr;
        std::reverse(newRev.begin(), newRev.end());
        for(auto p = words.begin(); p != words.end(); ) {
            if(newStr.find(*p) != std::string::npos || newRev.find(*p) != std::string::npos) {
                std::cerr << "Now subsumes: " << *p << std::endl;
                p = words.erase(p);
            } else {
                ++ p;
            }
        }

        std::cerr
            << "Words remaining: " << (words.size() + 1)
            << " Latest combination: (" << bestOverlap << ") " << newStr
            << std::endl;

        words.push_back(std::move(newStr));
        words.push_back(std::move(newRev));
        bestPossible = bestOverlap; // Merging existing words will never make longer merges possible
    }

    std::cerr
        << "After merging: " << words.size()
        << std::endl;

    // Remove all fully subsumed words (i.e. reversed words)

    for(auto p = words.begin(); p != words.end(); ) {
        bool subsumed = false;
        std::string rev = *p;
        std::reverse(rev.begin(), rev.end());
        for(auto i = words.begin(); i != words.end(); ++ i) {
            if(i == p) {
                continue;
            }
            if(i->find(*p) != std::string::npos || i->find(rev) != std::string::npos) {
                subsumed = true;
                break;
            }
        }
        if(subsumed) {
            p = words.erase(p);
        } else {
            ++ p;
        }
    }

    std::cerr
        << "After subsuming: " << words.size()
        << std::endl;

    // Sort words longest-to-shortest for display
    std::sort(words.begin(), words.end(), [](const std::string &a, const std::string &b) {
        return a.size() > b.size();
    });

    std::size_t len = 0;
    for(const auto &word : words) {
        std::cout
            << word
            << std::endl;
        len += word.size();
    }
    std::cerr
        << "Total size: " << len
        << std::endl;
    return 0;
}

#include <iostream>
#include <string>
#include <vector>
#include <unordered_map>
#include <unordered_set>
#include <limits>

class vec2 {
public:
    int x;
    int y;

    vec2(void) : x(0), y(0) {};
    vec2(int x, int y) : x(x), y(y) {}

    bool operator ==(const vec2 &b) const {
        return x == b.x && y == b.y;
    }

    vec2 &operator +=(const vec2 &b) {
        x += b.x;
        y += b.y;
        return *this;
    }

    vec2 &operator -=(const vec2 &b) {
        x -= b.x;
        y -= b.y;
        return *this;
    }

    vec2 operator +(const vec2 b) const {
        return vec2(x + b.x, y + b.y);
    }

    vec2 operator *(const int b) const {
        return vec2(x * b, y * b);
    }
};

class box2 {
public:
    vec2 tl;
    vec2 br;

    box2(void) : tl(), br() {};
    box2(vec2 a, vec2 b)
        : tl(std::min(a.x, b.x), std::min(a.y, b.y))
        , br(std::max(a.x, b.x) + 1, std::max(a.y, b.y) + 1)
    {}

    void grow(const box2 &b) {
        if(b.tl.x < tl.x) {
            tl.x = b.tl.x;
        }
        if(b.br.x > br.x) {
            br.x = b.br.x;
        }
        if(b.tl.y < tl.y) {
            tl.y = b.tl.y;
        }
        if(b.br.y > br.y) {
            br.y = b.br.y;
        }
    }

    bool intersects(const box2 &b) const {
        return (
            ((tl.x >= b.br.x) != (br.x > b.tl.x)) &&
            ((tl.y >= b.br.y) != (br.y > b.tl.y))
        );
    }

    box2 &operator +=(const vec2 b) {
        tl += b;
        br += b;
        return *this;
    }

    int width(void) const {
        return br.x - tl.x;
    }

    int height(void) const {
        return br.y - tl.y;
    }

    int maxdim(void) const {
        return std::max(width(), height());
    }
};

template <> struct std::hash<vec2> {
    std::size_t operator ()(const vec2 &o) const {
        return std::hash<int>()(o.x) + std::hash<int>()(o.y) * 997;
    }
};

template <class A,class B> struct std::hash<std::pair<A,B>> {
    std::size_t operator ()(const std::pair<A,B> &o) const {
        return std::hash<A>()(o.first) + std::hash<B>()(o.second) * 31;
    }
};

class word_placement {
public:
    vec2 start;
    vec2 dir;
    box2 bounds;
    const std::string *word;

    word_placement(vec2 start, vec2 dir, const std::string *word)
        : start(start)
        , dir(dir)
        , bounds(start, start + dir * (word->size() - 1))
        , word(word)
    {}

    word_placement(vec2 start, const word_placement &copy)
        : start(copy.start + start)
        , dir(copy.dir)
        , bounds(copy.bounds)
        , word(copy.word)
    {
        bounds += start;
    }

    word_placement(const word_placement &copy)
        : start(copy.start)
        , dir(copy.dir)
        , bounds(copy.bounds)
        , word(copy.word)
    {}
};

class word_placement_links {
public:
    std::unordered_set<word_placement*> placements;
    std::unordered_set<std::pair<char,word_placement*>> relativePlacements;
};

class grid {
public:
    std::vector<std::string> wordCache; // Just a block of memory for our pointers to reference
    std::unordered_map<vec2,char> state;
    std::unordered_set<word_placement*> placements;
    std::unordered_map<const std::string*,word_placement_links> wordPlacements;
    std::unordered_map<char,std::unordered_set<word_placement*>> relativeWordPlacements;
    box2 bound;

    grid(const std::vector<std::string> &words) {
        wordCache = words;
        std::vector<vec2> directions;
        directions.emplace_back(+1,  0);
        directions.emplace_back(+1, +1);
        directions.emplace_back( 0, +1);
        directions.emplace_back(-1, +1);
        directions.emplace_back(-1,  0);
        directions.emplace_back(-1, -1);
        directions.emplace_back( 0, -1);
        directions.emplace_back(+1, -1);

        wordPlacements.reserve(wordCache.size());
        placements.reserve(wordCache.size());
        relativeWordPlacements.reserve(64);

        std::size_t total = 0;
        for(const std::string &word : wordCache) {
            word_placement_links &p = wordPlacements[&word];
            p.placements.reserve(8);
            auto &rp = p.relativePlacements;
            std::size_t l = word.size();
            rp.reserve(l * directions.size());
            for(int i = 0; i < l; ++ i) {
                for(const vec2 &d : directions) {
                    word_placement *rwp = new word_placement(d * -i, d, &word);
                    rp.emplace(word[i], rwp);
                    relativeWordPlacements[word[i]].insert(rwp);
                }
            }
            total += l;
        }
        state.reserve(total);
    }

    const std::string *find_word(const std::string &word) const {
        for(const std::string &w : wordCache) {
            if(w == word) {
                return &w;
            }
        }
        throw std::string("Failed to find word in cache");
    }

    void remove_word(const std::string *word) {
        const word_placement_links &links = wordPlacements[word];
        for(word_placement *p : links.placements) {
            placements.erase(p);
            delete p;
        }
        for(auto &p : links.relativePlacements) {
            relativeWordPlacements[p.first].erase(p.second);
            delete p.second;
        }
        wordPlacements.erase(word);
    }

    void remove_placement(word_placement *placement) {
        wordPlacements[placement->word].placements.erase(placement);
        placements.erase(placement);
        delete placement;
    }

    bool check_placement(const word_placement &placement) const {
        vec2 p = placement.start;
        for(const char c : *placement.word) {
            auto i = state.find(p);
            if(i != state.end() && i->second != c) {
                return false;
            }
            p += placement.dir;
        }
        return true;
    }

    int check_new(const word_placement &placement) const {
        int n = 0;
        vec2 p = placement.start;
        for(const char c : *placement.word) {
            n += !state.count(p);
            p += placement.dir;
        }
        return n;
    }

    void check_placements(const box2 &b) {
        for(auto i = placements.begin(); i != placements.end(); ) {
            if(!b.intersects((*i)->bounds) || check_placement(**i)) {
                ++ i;
            } else {
                i = placements.erase(i);
            }
        }
    }

    void add_placement(const vec2 p, const word_placement &relative) {
        word_placement check(p, relative);
        if(check_placement(check)) {
            word_placement *wp = new word_placement(check);
            placements.insert(wp);
            wordPlacements[relative.word].placements.insert(wp);
        }
    }

    void place(word_placement placement) {
        remove_word(placement.word);
        int overlap = 0;
        for(const char c : *placement.word) {
            char &g = state[placement.start];
            if(g == '\0') {
                g = c;
                for(const word_placement *rp : relativeWordPlacements[c]) {
                    add_placement(placement.start, *rp);
                }
            } else if(g != c) {
                throw std::string("New word changes an existing character!");
            } else {
                ++ overlap;
            }
            placement.start += placement.dir;
        }
        bound.grow(placement.bounds);
        check_placements(placement.bounds);

        std::cerr
            << draw('.', "\n")
            << "Added " << *placement.word << " (overlap: " << overlap << ")"
            << ", Grid: " << bound.width() << "x" << bound.height() << " of " << state.size() << " chars"
            << ", Words remaining: " << wordPlacements.size()
            << std::endl;
    }

    int check_cost(box2 b) const {
        b.grow(bound);
        return (
            ((b.maxdim() - bound.maxdim()) << 16) |
            (b.width() + b.height() - bound.width() - bound.height())
        );
    }

    void add_next(void) {
        int bestNew = std::numeric_limits<int>::max();
        int bestCost = std::numeric_limits<int>::max();
        int bestLen = 0;
        word_placement *best = nullptr;
        for(word_placement *p : placements) {
            int n = check_new(*p);
            if(n <= bestNew) {
                int l = p->word->size();
                int cost = check_cost(box2(p->start, p->start + p->dir * l));
                if(n < bestNew || cost < bestCost || (cost == bestCost && l < bestLen)) {
                    bestNew = n;
                    bestCost = cost;
                    bestLen = l;
                    best = p;
                }
            }
        }
        if(best == nullptr) {
            throw std::string("Failed to find join to existing blob");
        }
        place(*best);
    }

    void fill(void) {
        while(!placements.empty()) {
            add_next();
        }
    }

    std::string draw(char blank, const std::string &linesep) const {
        std::string result;
        result.reserve((bound.width() + linesep.size()) * bound.height());
        for(int y = bound.tl.y; y < bound.br.y; ++ y) {
            for(int x = bound.tl.x; x < bound.br.x; ++ x) {
                auto c = state.find(vec2(x, y));
                result.push_back((c == state.end()) ? blank : c->second);
            }
            result.append(linesep);
        }
        return result;
    }

    box2 bounds(void) const {
        return bound;
    }

    int chars(void) const {
        return state.size();
    }
};

int main(int argc, const char *const *argv) {
    std::vector<std::string> words;

    // Load all words from input
    while(true) {
        std::string word;
        std::getline(std::cin, word);
        if(word.empty()) {
            break;
        }
        words.push_back(std::move(word));
    }

    std::cerr
        << "Input word count: " << words.size() << std::endl;

    // initialise grid
    grid g(words);

    // add first word (order of input file means this is longest word)
    g.place(word_placement(vec2(0, 0), vec2(1, 0), g.find_word(words.front())));

    // add all other words
    g.fill();

    std::cout << g.draw('.', "\n");

    int w = g.bounds().width();
    int h = g.bounds().height();
    int n = g.chars();
    std::cerr
        << "Final grid: " << w << "x" << h
        << " with " << n << " characters"
        << " (" << (n * 100.0 / (w * h)) << "% filled)"
        << std::endl;
    return 0;
}

And finally, the result:

Alternative result (after fixing a couple of bugs in the program which were biasing certain directions and tweaking the cost function, I got a more compact but less optimal solution): 29275 chars, 198x195 (75.8% filled):

Again I haven't done much to optimise these programs, so it takes a while. But you can watch as it fills in the grid, which is quite hypnotic.

C++, 34191 character "grid" (with minimal human intervention, 6 or 7 can easily be saved)
This should be taken more as a bound for the 2D case, because the answer is still a 1D string. It's just my code from the previous challenge, but with the new ability to reverse any string. This gives us a lot more scope for combining words (particularly because it caps the worst case of non-overlapping superstrings to 26; one for each letter of the alphabet).
For some slight 2D visual appeal, it puts linebreaks in the result if it can do so for free (i.e. between 0-overlap words).
Pretty slow (still no caching). Here's the code:
#include <iostream>
#include <string>
#include <vector>
#include <algorithm>

std::size_t calcOverlap(const std::string &a, const std::string &b, std::size_t limit, std::size_t minimal) {
    std::size_t la = a.size();
    for(std::size_t p = std::min(std::min(la, b.size()), limit + 1); -- p > minimal; ) {
        if(a.compare(la - p, p, b, 0, p) == 0) {
            return p;
        }
    }
    return 0;
}

bool isSameReversed(const std::string &a, const std::string &b) {
    std::size_t l = a.size();
    if(b.size() != l) {
        return false;
    }
    for(std::size_t i = 0; i < l; ++ i) {
        if(a[i] != b[l-i-1]) {
            return false;
        }
    }
    return true;
}

int main() {
    std::vector<std::string> words;

    // Load all words from input and their reverses (words can be backwards now)
    while(true) {
        std::string word;
        std::getline(std::cin, word);
        if(word.empty()) {
            break;
        }
        words.push_back(word);
        std::reverse(word.begin(), word.end());
        words.push_back(std::move(word));
    }

    std::cerr
        << "Input word count: " << words.size() << std::endl;

    // Remove all fully subsumed words

    for(auto p = words.begin(); p != words.end(); ) {
        bool subsumed = false;
        for(auto i = words.begin(); i != words.end(); ++ i) {
            if(i == p) {
                continue;
            }
            if(i->find(*p) != std::string::npos) {
                subsumed = true;
                break;
            }
        }
        if(subsumed) {
            p = words.erase(p);
        } else {
            ++ p;
        }
    }

    std::cerr
        << "After subsuming checks: " << words.size()
        << std::endl;

    // Sort words longest-to-shortest (not necessary but doesn't hurt. Makes finding maxlen a tiny bit easier)
    std::sort(words.begin(), words.end(), [](const std::string &a, const std::string &b) {
        return a.size() > b.size();
    });

    std::size_t maxlen = words.front().size();

    // Repeatedly combine most-compatible words until we have only 1 word left (+ its reverse)
    std::size_t bestPossible = maxlen - 1;
    while(words.size() > 2) {
        auto bestA = words.begin();
        auto bestB = -- words.end();
        std::size_t bestOverlap = 0;
        for(auto p = ++ words.begin(), e = words.end(); p != e; ++ p) {
            if(p->size() - 1 <= bestOverlap) {
                continue;
            }
            for(auto q = words.begin(); q != p; ++ q) {
                std::size_t overlap = calcOverlap(*p, *q, bestPossible, bestOverlap);
                if(overlap > bestOverlap && !isSameReversed(*p, *q)) {
                    bestA = p;
                    bestB = q;
                    bestOverlap = overlap;
                }
                overlap = calcOverlap(*q, *p, bestPossible, bestOverlap);
                if(overlap > bestOverlap && !isSameReversed(*p, *q)) {
                    bestA = q;
                    bestB = p;
                    bestOverlap = overlap;
                }
            }
            if(bestOverlap == bestPossible) {
                break;
            }
        }
        std::string newStr = std::move(*bestA);
        if(bestOverlap == 0) {
            newStr.push_back('\n');
        }
        newStr.append(*bestB, bestOverlap, std::string::npos);

        if(bestA == -- words.end()) {
            words.pop_back();
            *bestB = std::move(words.back());
            words.pop_back();
        } else {
            *bestB = std::move(words.back());
            words.pop_back();
            *bestA = std::move(words.back());
            words.pop_back();
        }

        // Remove any words which are now in the result (forward or reverse)
        // (would not be necessary if we didn't have the reversed forms too)
        std::string newRev = newStr;
        std::reverse(newRev.begin(), newRev.end());
        for(auto p = words.begin(); p != words.end(); ) {
            if(newStr.find(*p) != std::string::npos || newRev.find(*p) != std::string::npos) {
                std::cerr << "Now subsumes: " << *p << std::endl;
                p = words.erase(p);
            } else {
                ++ p;
            }
        }

        std::cerr
            << "Words remaining: " << (words.size() + 1)
            << " Latest combination: (" << bestOverlap << ") " << newStr
            << std::endl;

        words.push_back(std::move(newStr));
        words.push_back(std::move(newRev));
        bestPossible = bestOverlap; // Merging existing words will never make longer merges possible
    }

    std::cerr
        << "After non-trivial merging: " << words.size()
        << std::endl;

    if(words.size() == 2 && !isSameReversed(words.front(), words.back())) {
        // must be 2 palindromes, so just join them
        words.front().append(words.back());
    }

    std::string result = words.front();

    std::cout
        << result
        << std::endl;
    std::cerr
        << "Word size: " << result.size() // Note this number includes newlines, so to get the grid size according to the rules, subtract newlines manually
        << std::endl;
    return 0;
}

Result: http://pastebin.com/UTe2WMcz (4081 characters less than the previous challenge)
It's pretty clear that some trivial savings can be made by putting the xd and wv lines vertical, intersecting the monster line. Then hhidetautisbneudui can intersect with the d, and lxwwwowaxocnnaesdda with w. This saves 4 characters. nbcllilhn can be substituted in to an existing s overlap (if one can be found) to save another 2 (or just 1 if no such overlap exists and it must be added vertically instead). Finally mjjrajaytq can be added vertically somewhere to save 1. This means with minimal human intervention, 6–7 characters can be saved from the result.
I would like to get this into 2D with the following method, but I'm struggling to find a way to implement it without making the algorithm O(n^4), which is quite impractical to compute!

Run the algorithm as above, but stop short when the overlaps reach 1 character
Repeatedly:

Find a group of 4 words which can be arranged into a rectangle
Add as many words as possible on top of this rectangle where each word overlaps at least 2 characters of the current shape (check all 8 directions) — this is the only stage where we can actually get an advantage over the current code

Combine the resulting grids and lone-words looking for single-letter overlaps each time

PHP
this one does the job theroratically; but 10000 are probably too many words for recursion. The script is running now. (still ran 24 hours later)
works fine on small directories, but I may make an iterative version next week.
$f=array("pen","op","po","ne","pro","aaa","abcd","dcba");
will output
abcd
apen
arop
ao..
although this is not an optimal result (scoring was changed ... I´m working on a generator). One optimal result is this:
open
.r.a
.o.a
dcba`
It is also not very fast; only removes substrings and sorts the remains by length,
the rest is brute force: try to fit the words into a rectangle, try on a larger rectangle if it fails.
btw: The substring part needs 4.5 minutes on my machine for the large directory
and cuts it down to 6,190 words; the sort on them takes 11 seconds.
$f=file('https://raw.githubusercontent.com/first20hours/google-10000-english/master/google-10000-english.txt');
// A: remove substrings - forward or reversed
$s=join(' ',$f);
$haystack="$s ".strrev($s);
foreach($f as$w)
{
    $r=strrev($w=trim($w)); // remove trailing line break and create reverse word
    if(!preg_match("%$w\w|\w$w%",$haystack)
        // no substr match ... now: is the reverse word in the list?
        // if so, keep only the lower one (ascii values)
        &!($w>$r&&strstr($s,$r))
        // strstr does NOT render the reverse substr regex obsolete:
        // this is only executed for $w=abc, not for $w=bca!
    )
        $g[]=$w
    ;
}

// B: sort the words by length
usort($g,function($a,$b){return strlen($a)-strlen($b);});

// C1: function to fit $words into $map
function gomap($words,$map)
{
    $h=count($map);$w=strlen($map[0]);
    $len=strlen($word=array_pop($words));
    // $x,$y=position; $d=0:horizontal, $d=1:vertical; $r=0: word, $r=1: reverse word
    for($x=$w-$len;$x>=0;$x--)for($y=$h-$len;$y>=0;$y--)for($d=0;$d<2;$d++)for($r=0;$r<2;$r++)
    {
        // does the word fit there?
        $drow=$r?strrev($word):$word;
        for($ok=1,$i=0;$ok&$i<$len;$i++)
            $ok=in_array($map[$y+$d*$i][$x+$i-$d*$i], [' ',$drow[$i]])
        ;
        // it does, paint it
        if($ok)
        {
            for($i=0;$i<$len;$i++)
                $map[$y+$d*$i][$x+$i-$d*$i]=$drow[$i];
            if(!count($words))      // this was the last word: return map
                return $map;
            else                    // there are more words: recurse
                if ($ok=gomap($words,$map))
                    return $ok;
            // no fit, try next position
        }
    }
    return 0;
}

// C2: rectangle loop
for($h=0;++$h;)for($w=0;$w++<$h;)   // define a rectangle
{
    // and try to fit the words in there
    if($map=gomap($g,
        array_fill(0,$h,str_repeat(' ',$w))
    ))
    {
        // words fit; output and break loops
        echo '<pre>',implode("\n",$map),'</pre>';
        break 2;
    }
}

Could you include an example when the program is run on a smaller dictionary?

I've actually changed the scoring (sorry!). The number of unused characters is not included in your score.

The looping here means this is ~O((w*h)^n). We know the solution will have something like 35k letters (from the last challenge), so it will end up calling gomap about 35000^6000 times. My calculator tells me that's "infinity". A better calculator tells me the actual number (https://www.wolframalpha.com/input/?i=35000%5E6000). Now, if we assume every atom in the universe is a 3 terrahertz processor dedicated to running this program, the universe will need to exist for 10^27154 times longer than it has so far before it completes. What I'm saying is: don't wait for it to finish!

all_licenses: Creative Commons - Attribution Share-Alike - https://creativecommons.org/licenses/by-sa/3.0/


text:
URL Golf Challenge #1: Reverse URLs with parts
This is the first part of a three part challenge. The next parts will be released on August 13 and 14.
This challenge series was inspired by another challenge series. You can view the original one at Random Golf of the Day #1: Shuffle an Array.

Instructions
Your goal in this code golf is to reverse a URL, but not fully.
Here we have a normal URL that we take as normal input:
https://codegolf.stackexchange.com/questions/45302/random-golf-of-the-day-1-shuffle-an-array

Here's the output:
random-golf-of-the-day-1-shuffle-an-array/45302/questions/com.stackexchange.codegolf//:http

As you can see, each part of the URL gets reversed. That example doesn't have parameters, so here's an example of that before reversing:
https://www.google.com/page.html?data=13

Here's the result:
13=data?page.html/com.google.www//:https

Rules

No standard loopholes.
No changing the URL in any way (removing http://, removing www., removing .html, etc.)

Scoring
This is code-golf, so shortest bytes wins that challenge. However, there is also a series-wide leaderboard that counts up the total of bytes used for your post in each challenge. You're only eligible for winning if you did all the 3 challenges.

Leaderboards
Below is the leaderboard for this challenge.

var QUESTION_ID=89670,OVERRIDE_USER=8478;function answersUrl(e){return"https://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"https://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="
"+e.body.replace(OVERRIDE_REG,"")+"
")});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;/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}
 <link rel="stylesheet" type="text/css" href="//cdn.sstatic.net/codegolf/all.css?v=83c949450c8b">

Leaderboard

	Author	Language	Size

Winners by Language

Language	User	Score

{{PLACE}}	{{NAME}}	{{LANGUAGE}}	{{SIZE}}	Link

{{LANGUAGE}}	{{NAME}}	{{SIZE}}	Link

To make sure that your answers show up, please start every answer with a headline, using the following Markdown template:
## Language Name, N bytes

where N is the size of your submission. If you improve your score, you can keep old scores in the headline, by striking them through. For instance:
## Ruby, <s>104</s> <s>101</s> 96 bytes

@FryAmTheEggman How is that related?

You should specify which chars are “part delimiters” (from your explanation, it looks like it’s :/?=)

I thought it was related because of the "part delimiters" as Lynn calls them, but I think it's actually not as close as I originally thought, so I've removed my comment.

This challenge seems very similar, I'm not sure it's a dupe, though. (The only differences seem to be how the "part delimiters" are handled and that spaces can appear?)

Answers here will look extremely similar to the ones in the question @FryAmTheEggman linked — split into parts where the delimiters are their own segment, reverse, and join. I feel this challenge isn’t sufficiently different (it’s really a slight simplification), so I’m closing this as a duplicate (unless someone disagrees).

source: Stack Exchange

license: Creative Commons - Attribution Share-Alike - https://creativecommons.org/licenses/by-sa/3.0/

sort: votes


text:
Automatic box expander
Introduction
Sometimes, my boxes are too small to fit anything in it. I need you to make a box expander! So, what makes a box a box in this challenge.
 OOOO
O    O
O    O
O    O
 OOOO

The corners of the box are always spaces. The box itself can be made out of the same character. That character can be any printable ASCII character, except a space. So, that's these characters:
!"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~

The side lengths of the box above are 4, 3. You may assume that the side length is always positive. That means that this is the smallest box you need to handle:
 #
# #
 #

In order to expand a box, you need to increment each side length. Let's go through this, step by step, with the above example. We first take the upper side of the box, which is:
 OOOO

We expand this by one, so we get:
 OOOOO

This is the upper and lower part of the box now. After that, we do the same with the sides on the left and right:
O
O
O

Becomes:
O
O
O
O

Now we reassemble the box, which results into:
 OOOOO
O     O
O     O
O     O
O     O
 OOOOO

The task
Given a box, expand it by 1. The box can be given in multiple lines, or in an array.
Test cases
 OOOO          OOOOO
O    O    >   O     O
 OOOO         O     O
               OOOOO

 XXXXXX        XXXXXXX
X      X  >   X       X
X      X      X       X
 XXXXXX       X       X
               XXXXXXX

 ~             ~~
~ ~       >   ~  ~
 ~            ~  ~
               ~~

This is code-golf, so the submission with the least amount of bytes wins!

can the box have a new line before it?

@Riley Yes, that is allowed :).

Can the box be padded with spaces?

@LeakyNun Yes, you may do that.

V, 6 5 bytes
yêpjÄ

Try it online!
This is actually a byte longer than it should be. It should have been:
äêjÄ

But this has an unknown bug. :(
Explanation:
yê     "yank this colum
  p    "paste what we just yanked
   j   "move down to line 2
    Ä  "and duplicate this line

What does the other one do?

@ConorO'Brien ä is the duplicate operator (essentially "y" and "p" together in one byte) so äê is "duplicate column"

Vim, 7 bytes
♥GYPjYp

where ♥ is Control-V.
           The cursor starts on the first non-whitespace character of the first line.
♥G         Enter visual block mode and go to bottom of document.
  YP       Duplicate this column.
    j      Move down to the second line of the file.
     Yp    Duplicate this line.

Why not use YP both times for consistency?

I accidentally hit p while recording the animation, so I stuck with it when transcribing the answer. Does it matter? >_>;

I just found the inconsistency odd, but I like your explanation.

This is exactly the same thing as my V answer, just that I happened to create one-byte mappings for <C-v> G and YP. It kinda makes my language feel cheap. :/

Hm, control-V shows up as a heart on my phone... ❤

@βετѧΛєҫαγ That’s because it is one! ♥ It’s just a placeholder, of course.

@Lynn Ohh haha. ❤

JavaScript (ES6), 57 53 52 bytes
s=>s.replace(/^.(.)/gm,s="$&$1").replace(/(\n.*)/,s)

Explanation: The first regexp duplicates the second column and the second regexp duplicates the second row, thus enlarging the box as desired. Edit: Saved 4 bytes thanks to MartinEnder♦.

Python, 49 42 bytes
Anonymous lambda:
-7 from xnor
lambda s:[t[:2]+t[1:]for t in s[:2]+s[1:]]

Previous version:
D=lambda s:s[:2]+s[1:]
lambda s:D(list(map(D,s)))

D is a function that duplicates the second item of a sequence.

The idea of re-using the function is clever, but it seems to be shorter to just repeat the code: lambda L:[s[:2]+s[1:]for s in L[:2]+L[1:]].

Side note for the previous version: I think map(D,D(s)) would give 43 instead

Retina, 20 bytes
Byte count assumes ISO 8859-1 encoding.
1`¶
¶$%'¶
%2=`.
$&$&

Try it online! (There's several additional lines which enable a test suite where the test cases are separated by two linefeeds.)
Explanation
1`¶
¶$%'¶

1 is a limit which restricts Retina to apply the substitution only to the first match it finds. ¶ matches a single linefeed, so we only need to consider replacing the linefeed at the end of the first line. It is replace with ¶$%'¶, where $%' inserts the entire line following the match (a Retina-specific substitution element). Hence, this duplicates the second line.
%2=`.
$&$&

Here, % is per-line mode, so each line is processed individually, and the lines are joined again afterwards. 2= is also a limit. This one means "apply the substitution only to the second match". The match itself is simple a single character and the substitution duplicates it. Hence, this stage duplicates the second column.

Haskell, 24 bytes

f(a:b:c)=a:b:b:c
f.map f

Uses RootTwo's idea of duplicating the second row and column. The map f does this to each row, and the f. then does this to the rows.

PowerShell v2+, 57 53 52 bytes
param($n)($n-replace'^.(.)','$&$1')[0,1+1..$n.count]

Slightly similar to Neil's JavaScript answer. The first replace matches the beginning of the line and the next two characters, and replaces them with the first character and second-character-twice. Instead of a second replace, it's swapped out for array-indexing to duplicate the second line. Takes input as an array of strings. The resultant array slices are left on the pipeline and printing is implicit.
Saved 4 bytes thanks to Martin.
Some examples:
PS C:\Tools\Scripts\golfing> .\automatic-box-expander.ps1 ' oooo ','o    o',' oooo '
 ooooo
o     o
o     o
 ooooo

PS C:\Tools\Scripts\golfing> .\automatic-box-expander.ps1 ' # ','# #',' # '
 ##
#  #
#  #
 ##

Brachylog, 28 26 bytes
2 bytes thanks to Fatalize.
{bB,?~c[A:C]hl2,A:Bc.}:1a.

Try it online!

This is two bytes shorter

@Fatalize I never knew you can do it that way...

MATL, 12 bytes
tZy"@:2hSY)!

Input is a 2D char array, with semicolon as row separator. For example, the first test case has input
[' OOOO ';'O    O';' OOOO ']

Try it online! Test cases 1, 2, 3.
Explanation
The code does the following twice: repeat the second row of the array and transpose.
To repeat the second row of an m×n array, the vector [1 2 2 3 ... m] is used as row index. This vector is generated as follows: range [1 2 3 ... m], attach another 2, sort.
t       % Take input implicitly. Duplicate
Zy      % Size of input as a two-element array [r, c]
"       % For each of r and c
  @     %   Push r in first iteration (or c in the second)
  :     %   Generate range [1 2 3 ... r] (or [1 2 3 ... c])
  2hS   %   Append another 2 and sort
  Y)    %   Apply as row index
  !     %   Transpose
        % End for. Display implicitly

K (ngn/k), 13 bytes
2{+(?x),1_x}/

Try it online!
?x removes duplicate rows of x, which in this case keeps all and only the first two rows. And 1_x drops the first row. So append those together with , to extend along the vertical dimension.
Then follow up with a transpose + to exchange vertical and horizontal, and do it all again 2{ }/ to also extend the horizontal.
J, 13 bytes
|:@(~.,}.)^:2

Try it online!
Exact same strategy but you spell it a little differently:

~. unique rows
}. drop first row
, append
|:@( ) then transpose
^:2 twice

SED 69 19 (14 + 1 for -r) 15
s/.(.)/&\1/;2p

Can't you just do /.\(.\)/\0\1;2p?

@Neil I looked all over for that 2p, I figured there was a way to do it, but I couldn't find it. Thanks!

The -r'' part is not needed as long as you add 1 byte for the r flag, thus saving 3 bytes. Also, since you edited your first version of the code, the explanation at the end is now not valid.

@Neil Couldn't believe my eyes when I saw the \0 backreference, since they start at 1. The GNU sed's online manual speaks none of it. However, using & is I think equivalent and shorter.

@seshoumara Ah, those regexp version subtleties... which one uses \0 then?

@Neil \0 did worked in sed, I just never seen it mentioned. Of the top of my head, vim uses and documents \0, and also & which do the same. I'm sure many other regex flavors might have it, but no other one comes to mind.

Pyth, 10 bytes
L+<b2tbyMy

Try it online!

Uiua 0.10.0, 11 10 bytes
⊡->1.⇡+1△.

Explanation + See it in action

CJam, 14 bytes
q~{~\_@]z}2*N*

Similar to my MATL answer, but repeats the second-last row instead of the second.
Try it online!
Explanation
q                e# Read input
 ~               e# Interpret as an array
  {      }2*     e# Do this twice
   ~             e# Dump array contents onto the stack
    \            e# Swap top two elements
     _           e# Duplicate
      @          e# Rotate
       ]         e# Pack into an array again
        z        e# Zip
            N*   e# Join by newlines. Implicitly display

APL, 17 15 bytes
{⍉⍵⌿⍨1+2=⍳≢⍵}⍣2

Test:
      smallbox largebox
┌───┬──────┐
│ # │ OOOO │
│# #│O    O│
│ # │O    O│
│   │O    O│
│   │ OOOO │
└───┴──────┘
      {⍉⍵⌿⍨1+2=⍳≢⍵}⍣2 ¨ smallbox largebox
┌────┬───────┐
│ ## │ OOOOO │
│#  #│O     O│
│#  #│O     O│
│ ## │O     O│
│    │O     O│
│    │ OOOOO │
└────┴───────┘

Explanation:
             ⍣2   run the following function 2 times:
{           }     stretch the box vertically and transpose
         ⍳≢⍵      indices of rows of box
       2=         bit-vector marking the 2nd row
  ⍵/⍨1+           replicate the 2nd row twice, all other rows once
 ⍉                transpose

The APL symbol monadic ⍉ is matrix transpose, which isn't the same thing as rotating by 90 degrees.

@JohnE: of course. I must've been more tired than I thought. Actually rotating by 90 degrees would be ⌽⍉ or ⊖⍉, but in this case it does not matter.

K, 15 bytes
2{+x@&1+1=!#x}/

Takes input as a matrix of characters:
  b: (" OOOO ";"O    O";" OOOO ")
(" OOOO "
 "O    O"
 " OOOO ")

Apply a function twice (2{…}/) which gives the transpose (+) of the right argument indexed (x@) by the incremental run-length decode (&) of one plus (1+) a list of the locations equal to 1 (1=) in the range from 0 up to (!) the size of the outer dimension of the right argument (#x).
Step by step,
  #b
3
  !#b
0 1 2
  1=!#b
0 1 0
  1+1=!#b
1 2 1
  &1+1=!#b
0 1 1 2
  b@&1+1=!#b
(" OOOO "
 "O    O"
 "O    O"
 " OOOO ")
  +b@&1+1=!#b
(" OO "
 "O  O"
 "O  O"
 "O  O"
 "O  O"
 " OO ")
  2{+x@&1+1=!#x}/b
(" OOOOO "
 "O     O"
 "O     O"
 " OOOOO ")

Try it here with oK.

Uiua SBCS, 11 bytes
⍥(⍉⊂◴:↘1.)2

Try it!
Port of algorithmshark's J and K answers.

05AB1E (legacy), 7 bytes
2FÀĆÁÁø

I/O as a list of strings.
Try it online or verify all test cases.
Explanation:
2F       # Loop 2 times:
  À      #  Rotate the rows in the list of strings once towards the left
         #  (which will use the implicit input-list in the first iteration)
   Ć     #  Enclose; append its own first item
    ÁÁ   #  Rotate the strings in the list twice towards the right
      ø  #  Zip/transpose; swapping rows/columns
         # (after the loop, the modified list of strings is output implicitly as result)

Uses the legacy version of 05AB1E, since ø works with lists of strings, whereas the new version of 05AB1E would require a matrix of characters.

R, 39 bytes
\(m,`?`=\(j)c(1:2,2:dim(m)[j]))m[?1,?2]

The version with a formatted output
Attempt This Online!
The function takes a matrix of characters m as an input.
Explanation:
\                   # declare our function
  (m,               # a matrix passed as an argument
     `?`=\(j)       # internal function `?` is declared inside of the argument field
                    # instead of main function body to spare curly brackets
c(1:2,2:dim(m)[j])) # the internal function body - outputs new matrix indices
          m[?1,?2]  # main function outputs the modified matrix with two 2nd rows
                    # and two 2nd columns

Dyalog APL, 14 bytes
(1 2,1↓⍳)¨∘⍴⌷⊢

(

1 2, {1, 2} prepended to
1↓ one element dropped from
⍳ the indices

)¨ of each

∘ of

⍴ the {row-count, column-count}

⌷ indexes into

⊢ the argument

E.g. for
 XX
X  X
 XX

we find the indices; {1, 2, 3} for the rows, and {1, 2, 3, 4} for the columns. Now we drop the initial elements to get {2, 3} and {2, 3, 4}, and then prepend with {1, 2}, giving {1, 2, 2, 3} and {1, 2, 2, 3, 4}. Finally, we use this to select rows and columns, simultaneously doubling row 2 and column 2.
TryAPL online!

ListSharp, 326 bytes
STRG a=READ[<here>+"\\a.txt"]
ROWS p=ROWSPLIT a BY ["\r\n"]
ROWS p=GETLINES p [1 TO p LENGTH-1]
ROWS p=p+p[1]+p[0]
STRG o=p[0]
ROWS y=EXTRACT COLLUM[2] FROM p SPLIT BY [""]
ROWS x=EXTRACT COLLUM[3] FROM p SPLIT BY [""]
[FOREACH NUMB IN 1 TO o LENGTH-1 AS i]
ROWS m=COMBINE[m,x] WITH [""]
ROWS m=COMBINE[y,m,y] WITH [""]
SHOW=m

I definitely need to add the nesting of functions, but this works very well
comment if you want an explanation

JavaScript, 160 146 141 bytes
s=>{a=s[1];r="";l=s.split("\n");m=l.length;n=l[0].length;for(i=0;i<=m;i++){for(j=0;j<=n;j++)r+=!(i%m)&&j%n||i%m&&!(j%n)?a:" ";r+="\n"}return r}

Ruby, 46 bytes
->a{a.map{|r|r.insert(2,r[1])}.insert(2,a[1])}

Very straighforward solution, taking input as array of lines. I don't like duplicated inserts, so will try to golf it.

C#, 127 124 bytes
s=>{int n=s.Count-1,i=0;s[0]=s[n]=s[0].Insert(1,s[0][1]+"");s.Insert(1,s[1]);for(;i++<n;)s[i]=s[i].Insert(1," ");return s;};

Compiles to a Func<List<string>, List<string>>.
Formatted version:
s =>
{
    int n = s.Count - 1, i = 0;

    s[0] = s[n] = s[0].Insert(1, s[0][1] + "");

    s.Insert(1, s[1]);

    for (; i++ < n;)
        s[i] = s[i].Insert(1, " ");

    return s;
};

Perl 5 + -pl -M5.10.0, 23 bytes
s/.\K./$&$&/;$.==2&&say
Try it online!

all_licenses: Creative Commons - Attribution Share-Alike - https://creativecommons.org/licenses/by-sa/4.0/


text:
Numbers of purity
Today we'll look at a sequence \$a\$, related to the Collatz function \$f\$:
$$f = \begin{cases}
n/2  & \text{if } n \equiv 0 \text{ (mod }2) \\
3n+1 & \text{if } n \equiv 1 \text{ (mod }2) \\
\end{cases}$$
We call a sequence of the form \$z, f(z), f(f(z)), …\$ a Collatz sequence.
The first number in our sequence, \$a(1)\$, is \$0\$. Under repeated application of \$f\$, it falls into a cycle \$0\to0\to0\to\:\cdots\$
The smallest number we haven't seen yet is 1, making \$a(2)=1\$. Under repeated application of \$f\$, it falls into a cycle \$1\to4\to2\to1\to\cdots\$
Now we have seen the number \$2\$ in the cycle above, so the next smallest number is \$a(3) = 3\$, falling into the cycle \$3\to10\to5\to16\to8\to4\to2\to1\to4\to\cdots\$
In all above cycles we've seen \$4\$ and \$5\$ already, so the next number is \$a(4) = 6\$.
By now you should get the idea. \$a(n)\$ is the smallest number that was not part of any Collatz sequences for all \$a(1), ..., a(n-1)\$
Write a program or function that, given an positive integer \$n\$, returns \$a(n)\$. Shortest code in bytes wins.

Testcases:
1  -> 0
2  -> 1
3  -> 3
4  -> 6
5  -> 7
6  -> 9
7  -> 12
8  -> 15
9  -> 18
10 -> 19
50 -> 114

(This is OEIS sequence A061641.)

Obligatory OEIS

Can input n be 0-based?

a(n+1) = a(n) odd: 3*a(n)+1, or a(n) even: a(n)/2

@LuisMendo Sorry, I somehow missed your message. No, reproduce the exact sequence as in the challenge.

If a isn't 0-based I don't understand why you seem to be "talking 0-based" here: a(n) is the smallest number that was not part of any Collatz sequences for all a(0), …, a(n − 1).

@daniero Fixed.

May our code logic assume the conjecture is true?

@xnor If you can prove it :P (In all seriousness, go ahead and post it, just mention it assumes the conjecture.)

Jelly, 20 19 bytes
ḟ@JḢ×3‘$HḂ?ÐĿ;Ṛ
Ç¡Ṫ

Try it online! or verify all test cases.
How it works
Ç¡Ṫ              Main link. No explicit arguments. Default argument: 0
 ¡               Read an integer n from STDIN and do the following n times.
Ç                  Call the helper link.
  Ṫ              Tail; extract the last element of the resulting array.

ḟ@JḢ×3‘$HḂ?ÐĿ;Ṛ  Helper link. Argument: A (array)

  J              Yield all 1-based indices of A, i.e., [1, ..., len(A)]. Since 0
                 belongs to A, there is at least one index that does belong to A.
ḟ@               Filter-false swapped; remove all indices that belong to A.
   Ḣ             Head; extract the first index (i) that hasn't been removed.
           ÐĿ    Call the quicklink to the left on i, then until the results are no
                 longer unique. Collect all unique results in an array.
         Ḃ?      If the last bit of the return value (r) is 1:
       $           Apply the monadic 3-link chain to the left to r.
    ×3‘              Yield 3r + 1.
        H        Else, halve r.
              Ṛ  Yield A, reversed.
             ;   Concatenate the results array with reversed A.

After n iterations, the value of a(n + 1) will be at the beginning of the array. Since we concatenate the new array with a reversed copy of the old one, this means that a(n) will be at the end.

Haskell, 93 92 bytes
c x|x<2=[[0,2]!!x]|odd x=x:c(3*x+1)|1<2=x:c(div x 2)
([y|y<-[-1..],all(/=y)$c=<<[0..y-1]]!!)

Usage example: ([y|y<-[-1..],all(/=y)$c=<<[0..y-1]]!!) 10 -> 19.
c x is the Collatz cycle for x with a little bit of cheating for x == 1. The main functions loops through all integers and keeps those that are not in c x for x in [0..y-1]. Pretty much a direct implementation of the definition. As Haskell index operator !! is 0-based, I start at -1 to prepend a (otherwise useless) number to get the index fixed.

MATL, 46 40 bytes
Oiq:"tX>Q:yX-X<`t0)to?3*Q}2/]h5M1>]Pv]0)

Try it online!
Explanation
The code has an outer for loop that generates n Collatz sequences, one in each iteration. Each sequence is generated by an inner do...while loop that computes new values and stores them in a sequence vector until a 1 or 0 is obtained. When we are done with the sequence, the vector is reversed and concatenated to a global vector that contains the values from all previous sequences. This vector may contain repeated values. The reversing of the sequence vector ensures that at the end of the outer loop the desired result (the starting value of the last sequence) will be at the end of the global vector.
Pseudo-code:
1  Initiallization
2  Generate n sequences (for loop):
3    Compute initial value for the k-th sequence
4    Generate the k-th sequence (do...while loop)
5      Starting from latest value so far, apply the Collatz algorithm to get next value
6      Update sequence with new value
7      Check if we are done. If so, exit loop. We have the k-th sequence
8    Update vector of seen values
9  We now have the n sequences. Get final result

Commented code:
O           % Push 0                                                          1
iq:         % Input n. Generate [1 2 ... n-1]                                 ·
"           % For loop: repeat n-1 times. Let k denote each iteration         2
  t         %   Duplicate vector of all seen values                           · 3
  X>Q       %   Take maximum, add 1                                           · ·
  :         %   Range from 1 to that: these are potential initial values      · ·
  y         %   Duplicate vector of all seen values                           · ·
  X-X<      %   Set difference, minimum: first value not seen                 · ·
  `         %   Do...while: this generates the k-th Collatz sequence          · 4
    t0)     %     Duplicate, push last value of the sequence so far           · · 5
    to      %     Duplicate, parity: 1 if odd, 0 if even                      · · ·
    ?       %     If odd                                                      · · ·
      3*Q   %       Times 3, plus 1                                           · · ·
    }       %     Else                                                        · · ·
      2/    %       Half                                                      · · ·
    ]       %     End if                                                      · · ·
    h       %     Concatenate new value of the sequence                       · · 6
    5M      %     Push the new value again                                    · · 7
    1>      %     Does it exceed 1? This is the loop condition                · · ·
  ]         %   End do...while. The loops ends when we have reached 0 or 1    · ·
  P         %   Reverse the k-th Collatz sequence                             · 8
  v         %   Concatenate with vector of previously seen values             · ·
]           % End for                                                         ·
0)          % Take last value. Implicitly display.                            9

Python 2, 97 96 bytes
r,=s={-1}
exec'n=r=min({r+1,r+2,r+3}-s)\nwhile{n}-s:s|={n};n=(n/2,3*n+1)[n%2]\n'*input()
print r

Takes advantage of the fact that all multiples of 3 are pure. Test it on Ideone.
How it works
On the first line, r,=s={-1} sets s = {-1} (set) and r = -1.
Next we read an integer from STDIN, repeat a certain string that many times, then execute it. This is equivalent to the following Python code.
for _ in range(input())
    n=r=min({r+1,r+2,r+3}-s)
    while{n}-s:
        s|={n}
        n=(n/2,3*n+1)[n%2]

In each iteration, we start by finding the smallest member of {r+1, r+2, r+3} that does not belong to s. In the first iteration, this initializes r as 0.
In all subsequent runs, s may (and will) contain some of r+1, r+2 and r+3, but never all of them, since all multiples of 3 are pure. To verify this statement, observe that no multiple m of 3 is of the form 3k + 1. That leaves 2m as the only possible pre-image, which is also a multiple of 3. Thus, m cannot appear in the Collatz sequence of any number that is less than m, and is therefore pure.
After identifying r and initializing n, we apply the Collatz function with n=(n/2,3*n+1)[n%2], adding each intermediate value of n to the set s with s|={n}. Once we encounter a number n that is already in s, {n}-s will yield an empty set, and the iteration stops.
The last value of r is the desired element of the sequence.

To add to this, a proof that all multiples of 3 are pure. Look at any Collatz sequence modulo 3.
After any application of the 3x+1 rule, the modulo is 1. After the application of the x/2 rule, mod
1 becomes 2, and mod 2 becomes 1. Neither rule can generate a multiple of 3, unless the starting
value already was a bigger multiple of 3 that got halved. But those are bigger values not generated
yet, so n=0 (mod 3) => n is pure.

Brachylog, 70 67 65 63 62 bytes
,[]:?:1ih.
,0<=X=,?'eXg{t{:2/.*?|:3*+.}gB,(?.sB;?:Bc:2&.)}:?c.

Try it online!

Pyth, 32 bytes
VtQ=+Y.u?%N2h*3N/N2Z)=Zf!}TYhZ)Z

Test suite.

Java, 148 bytes
int a(int n){if(n<2)return 0;int f=a(n-1),b,i,c;do{f++;for(b=1,i=1;i<n;i++)for(c=i==2?4:a(i);c>1;c=c%2>0?c*3+1:c/2)b=c==f?0:b;}while(b<1);return f;}

Ideone it! (Warning: exponential complexity due to zero optimization.)
Converting it from a do...while loop to a for loop would be golfier, but I am having trouble doing so.
Golfing advice is welcome as usual.

Not much, but you can golf 1 byte off by changing for(b=1,i=1;i<n;i++) to for(b=1,i=0;++i<n;). Btw, I understand why your ideone is missing the test case for 50, but why does it also miss the 10? It can handle it without a problem.

@KevinCruijssen Because the formatting would be bad.

Not the best improvement but I didn't spent too much time... (147 bytes)
int a(int n){if(n<2)return 0;int f=a(n-1),b=0,i,c;for(;b<1;){f++;for(b=1,i=1;i<n;i++)for(c=i==2?4:a(i);c>1;c=c%2>0?c*3+1:c/2)b=c==f?0:b;}return f;}

Perl6, 96
my @s;my $a=0;map {while ($a=@s[$a]=$a%2??3*$a+1!!$a/2)>1 {};while @s[++$a] {}},2..slurp;$a.say;

Based on the Perl 5 answer. A bit longer since Perl6 syntax is less forgiving than Perl5 syntax, but I'll settle with this for now.

Mathematica, 134 bytes
f=If[EvenQ@#,#/2,3#+1]&;a@n_:=(b={i=c=0};While[i++<n-1,c=First[Range@Max[#+1]~Complement~#&@b];b=b~Union~NestWhileList[f,c,f@#>c&]];c)

Easier-to-read format:
f = If[EvenQ@#, #/2, 3#+1] &;                        Collatz function
a@n_ := (                                            defines a(n)
  b = {i = c = 0};                                   initializations
                                                       b is the growing sequence
                                                       of cycles already completed
  While[i++ < n - 1,                                 computes a(n) recursively
    c = First[Range@Max[# + 1]~Complement~# & @b];   smallest number not in b
    b = b~Union~NestWhileList[f, c, f@# > c &]       apply f to c repeatedly
                                                       until the answer is smaller
                                                       than c, then add this new
                                                       cycle to b
    ]
  ; c)                                                 output final value of c

PHP, 233 124 bytes
<?$n=$argv[1];for($c=[];$n--;){for($v=0;in_array($v,$c);)$v++;for(;$n&&!in_array($v,$c);$v=$v&1?3*$v+1:$v/2)$c[]=$v;}echo$v;

+4 for function:
function a($n){for($c=[];$n--;){for($v=0;in_array($v,$c);)$v++;for(;$n&&!in_array($v,$c);$v=$v&1?3*$v+1:$v/2)$c[]=$v;}return$v;}

Perl 5 - 74 bytes
map{0 while 1<($a=$c[$a]=$a%2?$a*3+1:$a/2);0 while$c[++$a]}2..<>;print$a+0

This is a pretty straightforward solution. It repeatedly applies the Collatz function to the variable $a and stores in the array @c that the value has been seen, then after reaching 0 or 1 it increments $a until it's a number that hasn't been seen yet. This is repeated a number of times equal to the input minus 2, and finally the value of $a is outputted.

license: Creative Commons - Attribution Share-Alike - https://creativecommons.org/licenses/by-sa/4.0/


text:
Alphabet Spiral
You are to write a program or function that outputs this exact text:
OPQRSTU
NQPONMV
MREDCLW
LSFABKX
KTGHIJY
JUVWXYZA
IHGFEDCB

May also be lowercase and trailing spaces and newlines are allowed.
Standard loopholes are disallowed
This is code-golf, so smallest byte count wins.

Can the lines have trailing spaces?

@Dennis Yes, I will edit the question to clarify that.

in case you wonder about the ascii codes of the spiral: http://puu.sh/qAitG/dd89c615cf.png

This output seems far too “complex” (in the Kolmogorov sense, i.e. devoid of pattern). Golf-y languages can hope to compress it, or in the case of MATL, hope to have a spiral built-in — but IMO it’s a bad sign if the best approach to a [tag:kolmogorov-complexity] challenge is ever “just print the output as a string literal” (see Python).

@Lynn Feel free to downvote if you think it's a bad question. That's what it's there for. I'll keep that in mind for next time though.

So I did; I was just explaining my vote. ^^

Strongly related.

MATL, 28 26 bytes
1Y2tIL)P0v7lYLP2:JTF-JQ&()

Try it online!
Explanation
1Y2     % Push string 'AB...Z'
tIL)    % Duplicate, remove last: gives 'AB...Y'
P       % Flip: gives 'YX...A'
0       % Push 0. Char 0 is treated as space
v       % Concatenate into a column vector of char
7lYL    % 7x7 numeric array: clockwise spiral of numbers from 1 to 49
P       % Flip vertically to make it anti-clockwise
2:      % Push [1 2]. Will be attached vertically after the last col
JqJh    % Index "[end-1, end]", to be used as row index
JQ      % Index "end+1", to be used as column index
&(      % Assign [1 2] to those positions. The rest of the new col will contain 0
)       % Index into the char vector. Indexing is modular and 1-based, so 0 as an
        % index refers to the last value of the char vector, which is char 0, or
        % equivalently space. So the 0 values in the numeric array give spaces.
        % Implicitly display

I have no idea how this works. Well done!

@DanTheMan I just added an explanation... I hope that helps!

I feel like the only language that could programmatically beat this is charcoal.

Python 2, 69 bytes
print'''OPQRSTU
NQPONMV
MREDCLW
LSFABKX
KTGHIJY
JUVWXYZA
IHGFEDCB'''

Just do it...

how boring. im afraid this would beat the actual answer in python tho..

Bubblegum, 47 bytes
A hex dump, courtesy of xxd:
00000000: 520a adf6 2e08 7444 c41e c8ff 4096 ca2d  R.....tD....@..-
00000010: 8832 2297 8cd7 1bb1 fe10 43ce 39cc 8e5f  .2".......C.9.._
00000020: 31f7 3d63 432e d3f3 3873 ecf7 7641 a3    1.=cC...8s..vA.

(This is just the spiral, “compressed”.)
Try it online.

Python 3, 72 bytes
print('OPQRSTU\nNQPONMV\nMREDCLW\nLSFABKX\nKTGHIJY\nJUVWXYZA\nIHGFEDCB')

Not exactly interesting, but it still works!

Use '''s instead of 's.

@EriktheGolfer It'd be a duplicate if he did that.

@mbomb007 I don't think so, because the other one is Python 2. Python 3 is different AFAIK, but, if it would be a dupe, I don't see why it isn't a dupe like this.

@EriktheGolfer When the only difference is a pair of parentheses and the version of the language, and the method used is identical, it's a duplicate. Just because versions are slightly different, doesn't mean you can provide the exact same answer in every version of a language without it being a duplicate.

@mbomb007 As I said before, why isn't this, as it is right now, a dupe then? The method is the same, the only differences are 1) Quoting type (does not change functionality) and 2) Parentheses, because the print statement is now a function, but that doesn't change anything, except that print is out of the builtins (and enhanced). So, why isn't this still a dupe then?

Fourier, 143 141 bytes
Not a particularly interesting answer except that it stores newlines (ASCII code 10) as the variable T... Uses isaacg's golfing program.
79a^a^a^a^a^a^a10a~T78a81avavavava86aTa77a82a69avava76a87aTa76a83a70a65a^a75a88aTa75a84a71a^a^a^a89a10a74a85a^a^a^a^a^a65aTa73avavavavavavava

Try it online
Try it online!

PHP, 130 bytes
for($p=30,$s=1,$r=_;$i<51;$r[$p]=chr(90-abs(25-$i++)),$p+=$s*(9-$d))if(!$n||!$c=++$c%$n)($d=8-$d)?$n++:$s=-$s;echo wordwrap($r,8);

literally draws the spiral. Run with -r
breakdown
for($p=30,$s=1,$r=_;    // initialize position to 3*9+3, $s(ign) to +1, $r(esult) to string
                        // implicit $c(ount)=$d(irection)=$i(ndex)=le($n)gth=0
    $i<51;              // loop $i from 0 to 50
    $r[$p]=chr(90-abs(25-$i++)),// 2. set current position in string to correct character
    $p+=$s*(9-$d))              // 3. move cursor
    if(!$n||!$c=++$c%$n)        // 1. if 1st iteration ($n==0) or ++$c has rotated around $n
        ($d=8-$d)                   // negate $direction (0=horizontal, 8=vertical)
            ?$n++                   // if vertical: increase length
            :$s=-$s                 // else invert sign
        ;
                        // output: wrap to lines of 8 characters
echo wordwrap($r,8);    // having left spaces avoids need to provide 3rd and 4th parameter

Easy to extend: For more alphabet,
append %50 after $i++,
replace 51 with the wanted number,
modify 8-$d, 9-$d, $p=30 and the wordwrap width to the new dimensions.

JavaScript ES6, 62 bytes
a=>`OPQRSTU
NQPONMV
MREDCLW
LSFABKX
KTGHIJY
JUVWXYZA
IHGFEDCB`

Try it

let f=a=>`OPQRSTU
NQPONMV
MREDCLW
LSFABKX
KTGHIJY
JUVWXYZA
IHGFEDCB`;

console.log(f());

Ruby, 63 bytes
In this case, nothing I found beats the boring answer...
puts"OPQRSTU
NQPONMV
MREDCLW
LSFABKX
KTGHIJY
JUVWXYZA
IHGFEDCB"

My best solution that didn't print the literal string only, but still has to directly hardcode some of it, is 110 bytes:
w=[*?B..?Q].reverse*''
puts [*?O..?U]*'',[?N,w[0,5],?V]*'',"MREDCLW
LSFABKX
KTGHIJY",[?J,*?U..?Z,?A]*'',w[8,8]

///, 57 bytes
OPQRSTU
NQPONMV
MREDCLW
LSFABKX
KTGHIJY
JUVWXYZA
IHGFEDCB

Try it online!
Just another boring answer. Move on!

Darn, I was 52561024 seconds late!

C#, 151 145 136 130 bytes
namespace a{class P{static void Main(){System.Console.Write("OPQRSTU\nNQPONMV\nMREDCLW\nLSFABKX\nKTGHIJY\nJUVWXYZA\nIHGFEDCB");}}}

Again, a boring one that does the trick.
Saved 6 bytes thanks to auhmaan.

You can save some bytes by replacing using System; ... Console.Write("...") with System.Console.Write("...")

You don't need a namespace. You can also compile to a Func<string> to save even more bytes: ()=>"OPQRSTU\nNQPONMV\nMREDCLW\nLSFABKX\nKTGHIJY\nJUVWXYZA\nIHGFEDCB";

You can save 5 bytes by using a string literal by prepending @ to the string before the quotes and then replacing the \n with literal new lines

You can save 2 more bytes by using namespace System and Console.Write instead of namespace a and System.Console.Write

Lua, 69 bytes
print([[OPQRSTU
NQPONMV
MREDCLW
LSFABKX
KTGHIJY
JUVWXYZA
IHGFEDCB]])

At least it works!

05AB1E, 46 bytes
•ûk2šÄ>¸©W$V-ÏfQ*LnHO”Ïmsqšƒ1ËUðhd@ý(™š•36B1¡»

Try it online!
Won't win by any means, but it does work.

Windows batch, 84 bytes
@for %%p in (OPQRSTU NQPONMV MREDCLW LSFABKX KTGHIJY JUVWXYZA IHGFEDCB) do @echo %%p

Simple enough for loop to loop through the strings.

Japt, 54 52 bytes
`opqÏ
nqpmv
mdclw
lsfabkx
ktgjy
juvwxyza
ihgfcb

Try it online

Explanation
Simply a compressed string of the spiral in lowercase.

Modern Pascal 2.0, 122 bytes
 Write('OPQRSTU'+#13#10+'NQPONMV'+#13#10+'MREDCLW'+#13#10+'LSFABKX'+#13#10+'KTGHIJY'+#13#10+'JUVWXYZA'+#13#10+'IHGFEDCB');

Explanation
Simply embed CRLF into the string, and display it.
// Author of Modern Pascal